<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Lucky Guy</title>
    <description>The latest articles on DEV Community by Lucky Guy (@luckyguy).</description>
    <link>https://dev.to/luckyguy</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4067064%2F1ab4c363-aee2-418c-9b43-a38cb0075278.png</url>
      <title>DEV Community: Lucky Guy</title>
      <link>https://dev.to/luckyguy</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/luckyguy"/>
    <language>en</language>
    <item>
      <title>How I Built a 100% Client-Side Text Repeater (And Why Your Data Never Leaves the Browser</title>
      <dc:creator>Lucky Guy</dc:creator>
      <pubDate>Tue, 11 Aug 2026 12:37:41 +0000</pubDate>
      <link>https://dev.to/luckyguy/how-i-built-a-100-client-side-text-repeater-and-why-your-data-never-leaves-the-browser-4ff5</link>
      <guid>https://dev.to/luckyguy/how-i-built-a-100-client-side-text-repeater-and-why-your-data-never-leaves-the-browser-4ff5</guid>
      <description>&lt;p&gt;A few weeks ago a friend asked me for a tool that could take a line like &lt;code&gt;"I Love You ❤️"&lt;/code&gt; and repeat it &lt;strong&gt;10,000 times&lt;/strong&gt; so he could paste it into a WhatsApp message. Easy, right? Just &lt;code&gt;string.repeat(10000)&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;But then he asked the follow-up that changed how I think about "simple" tools:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Please make sure it doesn't store my text anywhere. I don't want my private messages on your server."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That's when I realised a text repeater isn't really about repeating text at all. It's about &lt;strong&gt;privacy&lt;/strong&gt;. And the only way to genuinely guarantee it is to never send the data anywhere in the first place.&lt;/p&gt;

&lt;p&gt;So I built &lt;strong&gt;TextBolt&lt;/strong&gt; — a text repeater that runs &lt;strong&gt;100% in the browser&lt;/strong&gt;. No backend, no database, no analytics pinging home with your words. Let me walk you through the interesting bits.&lt;/p&gt;

&lt;h2&gt;
  
  
  The core: repeating without a server
&lt;/h2&gt;

&lt;p&gt;The heart of the tool is embarrassingly simple. There's no API endpoint — the heavy lifting happens in the browser:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;repeated&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;repeat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;count&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's it. &lt;code&gt;String.prototype.repeat()&lt;/code&gt; is one of the most underrated methods in JavaScript. It's &lt;em&gt;much&lt;/em&gt; faster than a manual &lt;code&gt;for&lt;/code&gt; loop because the engine implements it in native code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Slow-ish (still fine for small counts)&lt;/span&gt;
&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;out&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;""&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;100000&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nx"&gt;out&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// Fast, native&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;out&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;repeat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;100000&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For a 13-character string repeated 100,000 times, that's a ~1.3 MB string. &lt;code&gt;repeat()&lt;/code&gt; handles it in milliseconds.&lt;/p&gt;

&lt;h2&gt;
  
  
  The parts that were actually hard
&lt;/h2&gt;

&lt;p&gt;Repeating text is trivial. Everything &lt;em&gt;around&lt;/em&gt; it is where the real engineering lives:&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Handling 100,000 items without freezing the page
&lt;/h3&gt;

&lt;p&gt;A 1.3 MB string is fine in memory, but the moment you try to render it to the DOM naively, you freeze the tab. Two things saved me:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Virtualised output&lt;/strong&gt; — I don't render 100,000 DOM nodes. I render the final repeated string into a single &lt;code&gt;&amp;lt;pre&amp;gt;&lt;/code&gt;/&lt;code&gt;&amp;lt;textarea&amp;gt;&lt;/code&gt; and let the browser handle scrolling. Creating 100k individual elements is the mistake; a single text node isn't.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Deferred rendering&lt;/strong&gt; — heavy generation happens inside &lt;code&gt;requestAnimationFrame&lt;/code&gt; (or a microtask) so the input field stays responsive while the string is built.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;generate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;count&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;separator&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// Yield to the event loop so the UI never freezes&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;r&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;requestAnimationFrame&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;body&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;repeat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;count&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;input&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;repeat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;count&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;separator&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// or a smarter join&lt;/span&gt;
  &lt;span class="c1"&gt;// then write to a single text node&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2. Separators, prefixes and suffixes
&lt;/h3&gt;

&lt;p&gt;A repeat tool is useless if you can only produce &lt;code&gt;aaaaaa&lt;/code&gt;. Users want:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;a a a a a&lt;/code&gt; (space separator)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;a,a,a,a&lt;/code&gt; (comma separator)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;1. a&lt;/code&gt;, &lt;code&gt;2. a&lt;/code&gt;, &lt;code&gt;3. a&lt;/code&gt; (auto-numbering)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;--a--a--a&lt;/code&gt; (prefix/suffix on each line)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The trick is to generate the &lt;em&gt;final&lt;/em&gt; string once, not to build and append pieces in a loop. For the numbered variant:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;numbered&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;count&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;start&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;format&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;n&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;n&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;. `&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;lines&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;count&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;count&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;lines&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;start&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;lines&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Pre-allocating the array (&lt;code&gt;new Array(count)&lt;/code&gt;) avoids the repeated resizing that a &lt;code&gt;push&lt;/code&gt;-in-a-loop incurs.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Copy, download, and share
&lt;/h3&gt;

&lt;p&gt;Two APIs do most of the work:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;navigator.clipboard.writeText()&lt;/code&gt;&lt;/strong&gt; for one-click copy.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;Blob&lt;/code&gt; + object URLs&lt;/strong&gt; for &lt;code&gt;.txt&lt;/code&gt; downloads.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;download&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;filename&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;content&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;blob&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Blob&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="nx"&gt;content&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;text/plain;charset=utf-8&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;URL&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createObjectURL&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;blob&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;a&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createElement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;a&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;href&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;download&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;filename&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;click&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="nx"&gt;URL&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;revokeObjectURL&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For the WhatsApp share button it's just a &lt;code&gt;wa.me&lt;/code&gt; deep link — the URL length limits how much you can pass, so that's a fun constraint to work around for long strings.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why client-side is the whole point
&lt;/h2&gt;

&lt;p&gt;The nicest part is that this architecture &lt;strong&gt;makes privacy a feature, not a promise&lt;/strong&gt;. Because the text never hits my server, there's nothing for me (or an attacker, or a subpoena) to hand over. There's no request body to intercept. The "we don't store your data" line isn't marketing — it's a &lt;em&gt;consequence&lt;/em&gt; of the architecture.&lt;/p&gt;

&lt;p&gt;That's worth remembering: &lt;strong&gt;if you can push computation to the client, you don't need to be trusted with the data at all.&lt;/strong&gt; You've removed the attack surface entirely.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I'd do differently
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Web Worker for the extreme cases.&lt;/strong&gt; At 100k+ with heavy prefixes, even &lt;code&gt;repeat()&lt;/code&gt; plus string building can jank. Moving it to a &lt;code&gt;Web Worker&lt;/code&gt; would keep the main thread silky-smooth.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Streaming into the textarea&lt;/strong&gt; in chunks instead of one giant write, for the same reason.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Better memory hygiene&lt;/strong&gt; — clearing the big string (&lt;code&gt;result = ""&lt;/code&gt;) when the user changes input, so the browser doesn't hold onto multi-megabyte strings.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Try it, fork the idea
&lt;/h2&gt;

&lt;p&gt;If you want to poke at the real thing, the live tool is at &lt;strong&gt;&lt;a href="https://textbolt.net/" rel="noopener noreferrer"&gt;https://textbolt.net/&lt;/a&gt;&lt;/strong&gt; — you can repeat anything up to 100,000 times, try the fancy-text and blank-text generators, and it's all running in your browser. I built it as a standalone tool plus a few sibling tools (a character counter, a fancy-text generator).&lt;/p&gt;

&lt;p&gt;The whole takeaway for your own projects: &lt;strong&gt;before you spin up a backend, ask if you actually need one.&lt;/strong&gt; For a surprising number of "simple" utilities, the browser already has everything you need — and shipping zero servers is the best privacy story you can tell.&lt;/p&gt;

&lt;p&gt;If you build your own version, I'd genuinely love to see it. Drop a link in the comments.&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>webdev</category>
      <category>showdev</category>
      <category>beginners</category>
    </item>
    <item>
      <title>How I’m Building a Minecraft Geometry Planner for Circles, Domes and Large Builds</title>
      <dc:creator>Lucky Guy</dc:creator>
      <pubDate>Fri, 07 Aug 2026 07:29:00 +0000</pubDate>
      <link>https://dev.to/luckyguy/how-im-building-a-minecraft-geometry-planner-for-circles-domes-and-large-builds-jb2</link>
      <guid>https://dev.to/luckyguy/how-im-building-a-minecraft-geometry-planner-for-circles-domes-and-large-builds-jb2</guid>
      <description>&lt;h1&gt;
  
  
  How I’m Building a Minecraft Geometry Planner for Circles, Domes and Large Builds
&lt;/h1&gt;

&lt;p&gt;Minecraft builders often need to create circles, domes, towers and other shapes using square blocks.&lt;/p&gt;

&lt;p&gt;The hard part is not knowing that a circle is possible. The hard part is planning the block layout, keeping the centre aligned, estimating materials and handling large builds without losing count.&lt;/p&gt;

&lt;p&gt;I’m building MC Build Planner to make that workflow easier.&lt;/p&gt;

&lt;h2&gt;
  
  
  The problems I wanted to solve
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Choosing a useful circle diameter&lt;/li&gt;
&lt;li&gt;Handling odd and even centre points&lt;/li&gt;
&lt;li&gt;Planning material stacks before building&lt;/li&gt;
&lt;li&gt;Finding coordinate anchors for large projects&lt;/li&gt;
&lt;li&gt;Building spheres and domes layer by layer&lt;/li&gt;
&lt;li&gt;Avoiding unreadable giant grids for mega builds&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  How the planner approaches geometry
&lt;/h2&gt;

&lt;p&gt;For a small build, a visible block grid is useful. A builder can inspect the outline and copy the pattern directly.&lt;/p&gt;

&lt;p&gt;For larger builds, a massive grid becomes impractical. The better workflow is:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Set a centre coordinate&lt;/li&gt;
&lt;li&gt;Mark north, east, south and west anchors&lt;/li&gt;
&lt;li&gt;Build one repeatable quadrant&lt;/li&gt;
&lt;li&gt;Mirror the quadrant around the centre&lt;/li&gt;
&lt;li&gt;Track materials and progress&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Open-source geometry core
&lt;/h2&gt;

&lt;p&gt;The geometry helpers are public here:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/mubasharalidotcom/mc-build-planner-core" rel="noopener noreferrer"&gt;https://github.com/mubasharalidotcom/mc-build-planner-core&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The repository currently covers planning logic for circles, ovals, half circles, quarter circles, rings, spheres, domes, cylinders and cone roofs.&lt;/p&gt;

&lt;h2&gt;
  
  
  Live project
&lt;/h2&gt;

&lt;p&gt;The live planner is here:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://mcbuildplanner.com/" rel="noopener noreferrer"&gt;https://mcbuildplanner.com/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I’m still improving the builder workflow, especially for large structures and layer-by-layer plans.&lt;/p&gt;

&lt;p&gt;What shape-planning problem would you want a Minecraft tool to solve?&lt;/p&gt;

</description>
      <category>minecraft</category>
      <category>javascript</category>
      <category>webdev</category>
      <category>opensource</category>
    </item>
  </channel>
</rss>
