<?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>Forem: Chris</title>
    <description>The latest articles on Forem by Chris (@zwin).</description>
    <link>https://forem.com/zwin</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.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F2660473%2F684a8f79-cef3-47a2-84ca-c394aed660fc.png</url>
      <title>Forem: Chris</title>
      <link>https://forem.com/zwin</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/zwin"/>
    <language>en</language>
    <item>
      <title>Why does stylized “cursive text” even render on Instagram?</title>
      <dc:creator>Chris</dc:creator>
      <pubDate>Sat, 02 May 2026 04:26:45 +0000</pubDate>
      <link>https://forem.com/zwin/why-does-stylized-cursive-text-even-render-on-instagram-45jp</link>
      <guid>https://forem.com/zwin/why-does-stylized-cursive-text-even-render-on-instagram-45jp</guid>
      <description>&lt;p&gt;Scrolling through Unicode‑styled bios always raises the same question for me: we’re not uploading fonts, so how are those ornate characters surviving across every app and device? I spent some time poking at &lt;a href="https://cursive-generator.cc/" rel="noopener noreferrer"&gt;Cursive Generator&lt;/a&gt; to see what kind of engineering makes that happen, and it’s surprisingly old‑school — closer to a Unicode hack than a graphics pipeline.&lt;/p&gt;

&lt;h3&gt;
  
  
  Unicode as poor man’s font system
&lt;/h3&gt;

&lt;p&gt;When you type something there and pick “Script” or “Bold Gothic,” the site isn’t drawing custom glyphs; it’s remapping code points. Most of those styles live in the “Mathematical Script,” “Fraktur,” and “Double‑Struck” blocks (U+1D4xx, U+1D5xx, U+1D50x). The generator likely has a static lookup table: ASCII &lt;code&gt;a–z&lt;/code&gt; → corresponding decorative characters. When a letter doesn’t exist in that range, it probably falls back to the base Latin, which is why punctuation stays plain.&lt;/p&gt;

&lt;p&gt;That interpretation means the whole text field output is still ordinary strings, not rendered images — hence copy/paste works in Instagram or Discord without fonts installed. The key trick is selecting code points that the major OS fonts happen to ship with glyphs for, like &lt;code&gt;Cascadia&lt;/code&gt;, &lt;code&gt;SegoeUIEmoji&lt;/code&gt;, or &lt;code&gt;Apple Color Emoji&lt;/code&gt;. “Generates across platforms” is less magic, more exploitation of those built‑in fonts.&lt;/p&gt;

&lt;h3&gt;
  
  
  PNG generation hints
&lt;/h3&gt;

&lt;p&gt;The landing page mentions downloadable PNGs for “wedding” or “Etsy designs.” That points to a canvas‑based renderer separate from the Unicode mapping path. My guess is an &lt;code&gt;&amp;lt;canvas&amp;gt;&lt;/code&gt; or &lt;code&gt;&amp;lt;svg&amp;gt;&lt;/code&gt; layer that uses Google Fonts under OFL — probably dynamically loaded via the Google Fonts CSS API once the user clicks “Image.” The PNG export could be &lt;code&gt;canvas.toDataURL()&lt;/code&gt; or a &lt;code&gt;Blob&lt;/code&gt; converted for download.&lt;/p&gt;

&lt;p&gt;The absence of server latency (images appear instantly) implies client‑side rendering only; no headless Chrome round‑trip. Cloudflare presence in headers suggests purely for CDN + DDoS protection, not computation.&lt;/p&gt;

&lt;h3&gt;
  
  
  Astro front‑end observations
&lt;/h3&gt;

&lt;p&gt;The DOM fingerprints show Astro v6 and TailwindCSS. That fits the static‑site profile: layout generated at build time, minimal JS hydration except for the generator input logic. Given Astro’s islands model, the state machine for style selection is probably a reactive miniature component — maybe written in Svelte or React nested inside the Astro page. It doesn’t look like any heavy framework is present; just small hydration islands and a single script handling copy/download actions.&lt;/p&gt;

&lt;p&gt;One subtle optimization I noticed: loading “10 more styles” doesn’t re‑render the whole layout. That points to pre‑fetched fragments or data attributes toggled via DOM mutation instead of network fetches. Very low overhead, typical of Astro partial hydration.&lt;/p&gt;

&lt;h3&gt;
  
  
  Speculative internals
&lt;/h3&gt;

&lt;p&gt;Two small engineering guesses:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Lookup compression&lt;/strong&gt; — with 42 styles × ~60 supported characters, a naïve map would be thousands of entries. They might autogenerate this from Unicode ranges, not hardcode. Could be a JSON slice pre‑compiled during Astro build and embedded in the bundle.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;PNG font rasterization&lt;/strong&gt; — likely using &lt;code&gt;OffscreenCanvas&lt;/code&gt; plus &lt;code&gt;FontFace&lt;/code&gt; API. It would allow asynchronous font loading while keeping main‑thread responsive, explaining the lack of flicker when changing script styles.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  What I'm still curious about
&lt;/h3&gt;

&lt;p&gt;PNG exports seem crisp even at large sizes — possibly vectorized before rasterization. Is the download actually a rendered canvas bitmap, or are they producing SVG and then rasterizing via a hidden &lt;code&gt;&amp;lt;img&amp;gt;&lt;/code&gt; to get transparency? I couldn’t confirm from the client code alone. If it’s the latter, that’s a neat hybrid I’d like to see more of.&lt;/p&gt;

</description>
      <category>computerscience</category>
      <category>programming</category>
      <category>socialmedia</category>
      <category>webdev</category>
    </item>
    <item>
      <title>The bellows move, but the CPU stays quiet — what’s powering this browser based harmonium?</title>
      <dc:creator>Chris</dc:creator>
      <pubDate>Sun, 19 Apr 2026 12:11:48 +0000</pubDate>
      <link>https://forem.com/zwin/the-bellows-move-but-the-cpu-stays-quiet-whats-powering-this-browser-based-harmonium-l14</link>
      <guid>https://forem.com/zwin/the-bellows-move-but-the-cpu-stays-quiet-whats-powering-this-browser-based-harmonium-l14</guid>
      <description>&lt;h2&gt;
  
  
  The bellows move, but the CPU stays quiet — what’s powering this browser &lt;a href="https://web-harmonium.app" rel="noopener noreferrer"&gt;web harmonium&lt;/a&gt;?
&lt;/h2&gt;

&lt;p&gt;Clicking into this harmonium-in-a-tab feels a bit surreal: a little bellow handle animates, reeds respond to keypresses, and the sound is surprisingly organic for something coming out of &lt;code&gt;AudioContext&lt;/code&gt;. I'm less interested in how musical it feels than in how they pulled off that “real instrument in a browser” illusion with so little lag.&lt;/p&gt;

&lt;h3&gt;
  
  
  Probably sample playback, not synthesis
&lt;/h3&gt;

&lt;p&gt;The page claims “nine sampling points across three octaves,” which suggests pre‑recorded WAVs pitched via resampling rather than additive synthesis. My guess is each note name maps to an &lt;code&gt;AudioBuffer&lt;/code&gt; loaded once and stretched by &lt;code&gt;playbackRate&lt;/code&gt;. Because harmonium reeds are relatively static timbrally, a few samples per octave are enough if you interpolate pitch carefully — maybe &lt;code&gt;detune&lt;/code&gt; in 25‑cent steps.&lt;br&gt;&lt;br&gt;
What’s impressive is the lack of audible pops between bellows volume changes; that implies they’re smoothing gain adjustments through a shared &lt;code&gt;GainNode&lt;/code&gt; envelope, not tweaking volume directly on &lt;code&gt;AudioBufferSourceNode&lt;/code&gt;. The “pump handle” UI probably just changes a target amplitude, and a short exponential ramp handles the fade so the CPU doesn’t glitch.&lt;/p&gt;

&lt;h3&gt;
  
  
  MIDI velocity and the mystery of expression
&lt;/h3&gt;

&lt;p&gt;The velocity‑sensitive response hints at simple amplitude mapping, but the harmonium doesn’t actually respond to velocity in the acoustic sense — it’s air pressure. Maybe they map velocity onto the global bellows gain, making harder hits simulate stronger pressure. Without polyphonic aftertouch, that would still feel static if you hold notes. I’d bet they’re smoothing &lt;code&gt;gain.value&lt;/code&gt; over time to fake air compression.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;navigator.requestMIDIAccess&lt;/code&gt; API can be finicky in Safari, so I’d love to see what polyfill (if any) they use. The low latency suggests they rely on Chrome’s direct MIDI path and accept best‑effort elsewhere.&lt;/p&gt;

&lt;h3&gt;
  
  
  The “drone” is a separate audio graph
&lt;/h3&gt;

&lt;p&gt;Switching between the reed drone and tanpura loop is instant, no browser re‑buffering pause. That implies pre‑decoded looping buffers. The tanpura’s periodicity sounds about five seconds, so they likely run a looping &lt;code&gt;AudioBufferSourceNode&lt;/code&gt; with a small crossfade fade‑out/fade‑in at the seam. The harmonium drone could simply be the same sample layered under a low‑pass filter.&lt;br&gt;&lt;br&gt;
Given that it keeps playing while you record, the recorder must be grabbing from a mix &lt;code&gt;MediaStreamDestination&lt;/code&gt; feeding into a &lt;code&gt;MediaRecorder&lt;/code&gt; — straightforward but clever for an all‑client setup.&lt;/p&gt;

&lt;h3&gt;
  
  
  Recording and share links
&lt;/h3&gt;

&lt;p&gt;Recordings save as WebM up to two minutes, which lines up neatly with Chrome’s default &lt;code&gt;MediaRecorder&lt;/code&gt; memory limits; beyond that, latency spikes. The “one‑hour share link” that supposedly stores the state is probably a compressed JSON blob passed via base64 — about forty characters on my short test, so likely LZ‑compressed settings encoded into the query string. Interesting that playback settings, not audio, travel in that link; the recipient just rebuilds the same instrument state client‑side.&lt;/p&gt;

&lt;h3&gt;
  
  
  PWA and caching puzzle
&lt;/h3&gt;

&lt;p&gt;The thing installs offline, so they’ve gone the &lt;code&gt;serviceWorker&lt;/code&gt; route. But harmonium samples are big — three octaves × nine notes ≈ 27 files at maybe 300–500 KB each. That’s bandwidth‑heavy. Either Cloudflare is edge‑caching aggressively, or they lazy‑cache samples on first touch. Watching the network panel shows delayed fetches per note, supporting the lazy hypothesis. Makes sense: cache only notes actually played rather than forcing a multi‑megabyte warm‑up.&lt;/p&gt;

&lt;h3&gt;
  
  
  What I still wonder
&lt;/h3&gt;

&lt;p&gt;The bellows animation tracks volume with almost zero frame lag on mid‑range phones. Are they syncing that via an &lt;code&gt;AudioWorkletProcessor&lt;/code&gt; thread posting back amplitude RMS values, or just deducing handle position purely from UI state? If anyone has dug through its JavaScript bundle, I’m curious how tightly (or loosely) the visual and audio loops are coupled.&lt;/p&gt;

</description>
      <category>frontend</category>
      <category>javascript</category>
      <category>performance</category>
      <category>webdev</category>
    </item>
  </channel>
</rss>
