{"id":7779,"date":"2026-05-08T20:07:13","date_gmt":"2026-05-09T00:07:13","guid":{"rendered":"https:\/\/www.caskeys.com\/dc\/?p=7779"},"modified":"2026-05-09T20:58:11","modified_gmt":"2026-05-10T00:58:11","slug":"ring-buffer","status":"publish","type":"post","link":"https:\/\/www.caskeys.com\/dc\/ring-buffer\/","title":{"rendered":"Ring Buffer"},"content":{"rendered":"\n\n\n\n\n<h2 class=\"wp-block-heading\">Introduction<\/h2>\n\n\n\n<p>Ring buffers, also called circular buffers, circular queues, or cyclic buffers, are data structures where the end of the storage range wraps back around to the beginning. They are especially useful for First In, First Out (FIFO) patterns and continuous data streams. Ring buffers are a fundamental structure, and some early platforms even included hardware support for ring-buffer-style behavior.<\/p>\n\n\n\n<p>You have probably seen ring buffers at work without realizing it. Device inputs, such as keyboards, often queue commands through a ring buffer. Output streams use them too. Games commonly use ring buffers for player input history, internal state tracking, visual effects, and mechanics such as:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Serpentine or segmented entities<\/li>\n\n\n\n<li>Clone power-ups, such as <em>Ninja Gaiden II<\/em> on NES<\/li>\n\n\n\n<li>Slaves, options, and satellites in shoot \u2019em ups<\/li>\n<\/ul>\n\n\n\n<p>For example, in <em>Ninja Gaiden II<\/em>, Ryu\u2019s position is recorded into a 64-slot ring buffer as he moves. When Ryu has one spirit clone, the clone reads position data from slot 31 of the buffer. The second clone reads from slot 63. On screen, this creates the convincing illusion that the clones are tracing Ryu\u2019s movement.<\/p>\n\n\n\n<figure class=\"wp-block-image\"><a href=\"https:\/\/www.caskeys.com\/dc\/wp-content\/uploads\/2026\/05\/ring_buffer_example_0.png\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"425\" src=\"https:\/\/www.caskeys.com\/dc\/wp-content\/uploads\/2026\/05\/ring_buffer_example_0-1024x425.png\" alt=\"\" class=\"wp-image-7626\" srcset=\"https:\/\/www.caskeys.com\/dc\/wp-content\/uploads\/2026\/05\/ring_buffer_example_0-1024x425.png 1024w, https:\/\/www.caskeys.com\/dc\/wp-content\/uploads\/2026\/05\/ring_buffer_example_0-300x125.png 300w, https:\/\/www.caskeys.com\/dc\/wp-content\/uploads\/2026\/05\/ring_buffer_example_0-768x319.png 768w, https:\/\/www.caskeys.com\/dc\/wp-content\/uploads\/2026\/05\/ring_buffer_example_0.png 1515w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/a><figcaption class=\"wp-element-caption\"><em>Ninja Gaiden II<\/em> and <em>Salamander<\/em> demonstrate common examples of ring buffers in action. Their helper entities appear to trace player movements by binding themselves to older position data stored in predetermined buffer slots.<\/figcaption><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\">OpenBOR<\/h2>\n\n\n\n<p>In OpenBOR, ring buffers are usually built from indexed arrays.<\/p>\n\n\n\n<p>The secret sauce is an incremental cursor combined with modulo. Sometimes the cursor is an internal clock, but it is often simpler to keep a manual counter and increment it whenever the buffer updates. On each update, divide the cursor by the ring buffer size and keep the integer remainder. That remainder becomes the ring buffer index.<\/p>\n\n\n\n<p>Sounds complex? It is not. The whole operation is one short line using the modulo operator (<code>%<\/code>). Modulo returns the integer remainder after division, giving us a simple way to wrap numbers into a fixed range.<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: cpp; title: Code:; notranslate\" title=\"Code:\">\nint slot = cursor % BUFFER_SIZE; \/\/ Wrap cursor into valid buffer range.\n<\/pre><\/div>\n\n\n<p>If <code>BUFFER_SIZE<\/code> is <code>6<\/code>, then <code>cursor % BUFFER_SIZE<\/code> can only return values from 0 through 5.<\/p>\n\n\n\n<p>Here is what happens as cursor value increases:<\/p>\n\n\n\n<figure class=\"wp-block-table is-style-stripes\"><table class=\"has-fixed-layout\"><thead><tr><th>Cursor<\/th><th>Calculation<\/th><th>Resulting Slot<\/th><\/tr><\/thead><tbody><tr><td>0<\/td><td>0 % 6<\/td><td>0<\/td><\/tr><tr><td>1<\/td><td>1 % 6<\/td><td>1<\/td><\/tr><tr><td>2<\/td><td>2 % 6<\/td><td>2<\/td><\/tr><tr><td>3<\/td><td>3 % 6<\/td><td>3<\/td><\/tr><tr><td>4<\/td><td>4 % 6<\/td><td>4<\/td><\/tr><tr><td>5<\/td><td>5 % 6<\/td><td>5<\/td><\/tr><tr><td>6<\/td><td>6 % 6<\/td><td>0<\/td><\/tr><tr><td>7<\/td><td>7 % 6<\/td><td>1<\/td><\/tr><tr><td>8<\/td><td>8 % 6<\/td><td>2<\/td><\/tr><tr><td>9<\/td><td>9 % 6<\/td><td>3<\/td><\/tr><tr><td>10<\/td><td>10 % 6<\/td><td>4<\/td><\/tr><tr><td>11<\/td><td>11 % 6<\/td><td>5<\/td><\/tr><tr><td>12<\/td><td>12 % 6<\/td><td>0<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p>Notice how the slot wraps back to 0 after it reaches 5. That is the \u201cring\u201d in ring buffer. The cursor can keep counting upward forever, but modulo keeps the actual storage index inside the valid array range.<\/p>\n\n\n\n<p>Typical ring buffer write logic follows this pattern:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: cpp; title: Code:; notranslate\" title=\"Code:\">\n#define BUFFER_SIZE 6\n\n\/* Get existing cursor value and initialize to 0 if needed. *\/\nint cursor = get(data, DATA_CURSOR);\n\nif(typeof(cursor) != openborconstant(&quot;VT_INTEGER&quot;)) {\n     cursor = 0;\n}\n\n\/* Apply modulo to get target slot. *\/\nint slot = cursor % BUFFER_SIZE;\n\n\/* Write data. *\/\nset(data, DATA_START + slot, value);\n\n\/* Increment and write cursor value. *\/\ncursor++;\nset(data, DATA_CURSOR, cursor);\n<\/pre><\/div>\n\n\n<p>Each time this runs, the script writes to the next slot. Once it reaches the end of the buffer, it wraps around and starts overwriting the oldest entries. The full shadow trail example below uses this same pattern to record and draw position history.<\/p>\n\n\n\n<figure class=\"wp-block-embed is-type-wp-embed is-provider-dc-current wp-block-embed-dc-current\"><div class=\"wp-block-embed__wrapper\">\n<blockquote class=\"wp-embedded-content\" data-secret=\"tBNhMzbxVN\"><a href=\"https:\/\/www.caskeys.com\/dc\/shadow-trail-script\/\">Shadow Trail Sample<\/a><\/blockquote><iframe loading=\"lazy\" class=\"wp-embedded-content\" sandbox=\"allow-scripts\" security=\"restricted\" style=\"position: absolute; visibility: hidden;\" title=\"&#8220;Shadow Trail Sample&#8221; &#8212; DC Current\" src=\"https:\/\/www.caskeys.com\/dc\/shadow-trail-script\/embed\/#?secret=1kZBfnWkyi#?secret=tBNhMzbxVN\" data-secret=\"tBNhMzbxVN\" width=\"600\" height=\"338\" frameborder=\"0\" marginwidth=\"0\" marginheight=\"0\" scrolling=\"no\"><\/iframe>\n<\/div><\/figure>\n\n\n\n<p><strong>Tip:<\/strong> There\u2019s no reason the cursor can\u2019t live inside the ring buffer array. In fact, that&#8217;s usually the cleanest approach. This is how our shadow trail example above works, and it is why the flattened layout uses a start offset. The first array element stores the incremental cursor count, while the remaining elements hold the ring buffer data. Everything stays packaged together, avoids an external variable lookup, and keeps the structure organized.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code><strong><mark style=\"background-color:rgba(0, 0, 0, 0)\" class=\"has-inline-color has-luminous-vivid-orange-color\">&#091;cursor]<\/mark><\/strong>&#091;x0]&#091;y0]&#091;z0]&#091;sprite0]&#091;time0]&#091;x1]&#091;y1]&#091;z1]&#091;sprite1]&#091;time1]...<\/code><\/pre>\n\n\n\n<figure class=\"wp-block-image\"><a href=\"https:\/\/www.caskeys.com\/dc\/wp-content\/uploads\/2026\/05\/image-7.png\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"768\" src=\"https:\/\/www.caskeys.com\/dc\/wp-content\/uploads\/2026\/05\/image-7-1024x768.png\" alt=\"\" class=\"wp-image-7622\" srcset=\"https:\/\/www.caskeys.com\/dc\/wp-content\/uploads\/2026\/05\/image-7-1024x768.png 1024w, https:\/\/www.caskeys.com\/dc\/wp-content\/uploads\/2026\/05\/image-7-300x225.png 300w, https:\/\/www.caskeys.com\/dc\/wp-content\/uploads\/2026\/05\/image-7-768x576.png 768w, https:\/\/www.caskeys.com\/dc\/wp-content\/uploads\/2026\/05\/image-7.png 1448w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/a><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\">Advanced<\/h2>\n\n\n\n<p>If you want to get really cocky, constrain your buffer sizes to a power of two:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>2, 4, 8, 16, 32, 64, ... <\/code><\/pre>\n\n\n\n<p>Then you can use a bit mask instead of modulo.<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: cpp; title: Code:; notranslate\" title=\"Code:\">\n#define BUFFER_SIZE 8\n#define BUFFER_MASK (BUFFER_SIZE - 1)\n\nindex = cursor &amp; BUFFER_MASK;\ncursor++;\n<\/pre><\/div>\n\n\n<p>Why would you do this? Speed! <strong><em>Speeeeeeed!<\/em><\/strong><\/p>\n\n\n\n<p>Ring buffers are already fast, but bitwise AND is cheaper than division, and modulo is often implemented as division unless the compiler or runtime can optimize it away. On modern hardware, the difference is usually tiny. In a scripting environment like OpenBOR, it may not matter at all unless the operation is happening thousands of times per frame.<\/p>\n\n\n\n<p>Still, on older systems this could be a vital optimization. The Ninja Gaiden II example above uses 64 slots, which is exactly the kind of buffer size you would choose for this trick. A 64-slot buffer can be wrapped with a mask of 63:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: cpp; title: Code:; notranslate\" title=\"Code:\">\nindex = cursor &amp; 63;\n<\/pre><\/div>\n\n\n<p>The NES 6502 CPU has no native multiply or divide instruction, so tricks like this mattered. Instead of performing a costly software modulo operation, the code can use a simple bitwise AND &#8211; exactly the kind of operation CPUs are built for.<\/p>\n\n\n\n<p>Just remember the catch: this only works when the buffer size is a power of two. If your buffer size is 10, 20, 30, or any other non-power-of-two value, use modulo instead.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Considerations<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\">Advantages<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Fast<\/strong> &#8211; When built from an indexed array, ring buffers are extremely light. The modulo and array operations are simple enough for hot-path logic such as player input tracking, position history, and visual effects.<\/li>\n\n\n\n<li><strong>Efficient<\/strong> &#8211; Well-designed ring buffers can use less memory than other structures performing the same task. Their memory cost is also predictable. In the NES <em>Ninja Gaiden II<\/em> example, Ryu\u2019s position buffer uses only 128 bytes &#8211; less storage than this sentence in plain text, and roughly six percent of the NES\u2019s tiny 2KB of work RAM.<\/li>\n\n\n\n<li><strong>Simple<\/strong> &#8211; Ring buffers are easy to assemble once you understand the pattern. You do not need much script to make them work.<\/li>\n\n\n\n<li><strong>Expandable<\/strong> &#8211; Need smoother trails, more afterimages, or longer history? Increase the controlling buffer size constant and the same structure still works.<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">Drawbacks<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Static<\/strong> &#8211; Ring buffers do one job well, but they are not flexible general-purpose containers. When data needs to grow dynamically, another structure is usually a better fit.<\/li>\n\n\n\n<li><strong>Resident<\/strong> &#8211; Ring buffers are efficient, but their memory cost is constant. The storage is reserved whether every slot is currently useful or not.<\/li>\n\n\n\n<li><strong>Specialized<\/strong> &#8211; Ring buffers are ideal for certain tasks and poorly suited to many others. They shine when you need predictable, repeating access to recent history.<\/li>\n<\/ul>\n","protected":false},"excerpt":{"rendered":"<p>Learn how ring buffers work and how to use them in your OpenBOR projects.<\/p>\n","protected":false},"author":1,"featured_media":7787,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_jetpack_newsletter_access":"","_jetpack_dont_email_post_to_subs":false,"_jetpack_newsletter_tier_id":0,"_jetpack_memberships_contains_paywalled_content":false,"_jetpack_memberships_contains_paid_content":false,"footnotes":"","jetpack_publicize_message":"","jetpack_publicize_feature_enabled":false,"jetpack_social_post_already_shared":false,"jetpack_social_options":{"image_generator_settings":{"template":"highway","default_image_id":0,"font":"","enabled":false},"version":2},"jetpack_post_was_ever_published":false},"categories":[353,71],"tags":[],"class_list":["post-7779","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-gaming-and-fantasy","category-technology-temerity"],"jetpack_publicize_connections":[],"jetpack_featured_media_url":"https:\/\/www.caskeys.com\/dc\/wp-content\/uploads\/2026\/05\/ring_buffer_0.png","jetpack_sharing_enabled":true,"jetpack_shortlink":"https:\/\/wp.me\/p5lNM5-21t","jetpack_likes_enabled":true,"_links":{"self":[{"href":"https:\/\/www.caskeys.com\/dc\/wp-json\/wp\/v2\/posts\/7779","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.caskeys.com\/dc\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.caskeys.com\/dc\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.caskeys.com\/dc\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.caskeys.com\/dc\/wp-json\/wp\/v2\/comments?post=7779"}],"version-history":[{"count":17,"href":"https:\/\/www.caskeys.com\/dc\/wp-json\/wp\/v2\/posts\/7779\/revisions"}],"predecessor-version":[{"id":7827,"href":"https:\/\/www.caskeys.com\/dc\/wp-json\/wp\/v2\/posts\/7779\/revisions\/7827"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.caskeys.com\/dc\/wp-json\/wp\/v2\/media\/7787"}],"wp:attachment":[{"href":"https:\/\/www.caskeys.com\/dc\/wp-json\/wp\/v2\/media?parent=7779"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.caskeys.com\/dc\/wp-json\/wp\/v2\/categories?post=7779"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.caskeys.com\/dc\/wp-json\/wp\/v2\/tags?post=7779"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}