<?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: Nengil</title>
    <description>The latest articles on DEV Community by Nengil (@nengil).</description>
    <link>https://dev.to/nengil</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%2F3899451%2Fd578bbe6-6a8f-4fde-bb71-e76b82a270e4.jpg</url>
      <title>DEV Community: Nengil</title>
      <link>https://dev.to/nengil</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/nengil"/>
    <language>en</language>
    <item>
      <title>I just wanted to compress a confidential document. Instead I built 60 client-side file tools.</title>
      <dc:creator>Nengil</dc:creator>
      <pubDate>Mon, 27 Apr 2026 00:33:49 +0000</pubDate>
      <link>https://dev.to/nengil/i-just-wanted-to-compress-a-confidential-document-instead-i-built-60-client-side-file-tools-5454</link>
      <guid>https://dev.to/nengil/i-just-wanted-to-compress-a-confidential-document-instead-i-built-60-client-side-file-tools-5454</guid>
      <description>&lt;p&gt;It started with a small frustration. I needed to compress a few PDFs that contained personal information — bank statements, passport scans, the kind of thing you don't want sitting on a stranger's server.&lt;/p&gt;

&lt;p&gt;I opened the first "free PDF compressor" Google gave me. It uploaded my files. The progress bar was reassuring. The privacy policy was 8,000 words and reserved the right to "use uploaded content for service improvement."&lt;/p&gt;

&lt;p&gt;I closed the tab. Tried another. Same pattern. Tried a third. Same.&lt;/p&gt;

&lt;p&gt;Finally I opened a desktop tool, fought with the install, used it twice, never opened it again.&lt;/p&gt;

&lt;p&gt;A few weeks later I was converting a video for WhatsApp, then a HEIC photo from my phone, then a CSV to JSON, then trying to extract text from a screenshot. Different sites, different uploads, different cookie banners. The friction was constant.&lt;/p&gt;

&lt;p&gt;So I built &lt;strong&gt;The Local Kit&lt;/strong&gt; — 60 file tools that run entirely in your browser. No uploads. No signup. No tracking cookies. Files never touch a server.&lt;/p&gt;

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

&lt;p&gt;This post is the technical breakdown of how that's actually possible in 2026.&lt;/p&gt;

&lt;h2&gt;
  
  
  The architecture decision: client-side everything
&lt;/h2&gt;

&lt;p&gt;The defining choice was deciding that EVERY tool processes files locally. No "fast tier on server, slow tier on browser" hybrid. Just pure client-side.&lt;/p&gt;

&lt;p&gt;This sounds extreme but the reasoning is simple:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The moment you have a server processing user files, your privacy policy gets longer&lt;/li&gt;
&lt;li&gt;The moment you have a server, you have ongoing infrastructure cost&lt;/li&gt;
&lt;li&gt;The moment you have a server, you have to scale it&lt;/li&gt;
&lt;li&gt;The moment you have a server, your "no upload" promise is gone&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Modern browsers are powerful enough that 95% of file operations work great client-side. The remaining 5% (e.g., processing a 10GB video file) — I just don't support yet. Fair tradeoff.&lt;/p&gt;

&lt;h2&gt;
  
  
  The stack, tool by tool
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Video &amp;amp; audio: FFmpeg.wasm
&lt;/h3&gt;

&lt;p&gt;The hardest piece. FFmpeg compiled to WebAssembly, weighing about 25MB. Loads on demand the first time someone visits a video tool.&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="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;FFmpeg&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@ffmpeg/ffmpeg&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;ff&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;FFmpeg&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;ff&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;load&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;coreURL&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/vendor/ffmpeg-core.js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;wasmURL&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/vendor/ffmpeg-core.wasm&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;span class="c1"&gt;// Compress a video&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;ff&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;writeFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;input.mp4&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Uint8Array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;file&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;arrayBuffer&lt;/span&gt;&lt;span class="p"&gt;()));&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;ff&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;exec&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;-i&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;input.mp4&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;-c:v&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;libx264&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;-crf&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;28&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;output.mp4&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;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;ff&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;readFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;output.mp4&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Real FFmpeg, real H.264 encoding, real 10-line video compression — running on a phone CPU.&lt;/p&gt;

&lt;h3&gt;
  
  
  PDF: pdf-lib + pdf.js
&lt;/h3&gt;

&lt;p&gt;Two libraries, different jobs:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;pdf-lib&lt;/strong&gt; — generation, modification, merging, splitting&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;pdf.js&lt;/strong&gt; (Mozilla) — parsing, rendering, text extraction
&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;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;PDFDocument&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;pdf-lib&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;merged&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;PDFDocument&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&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;const&lt;/span&gt; &lt;span class="nx"&gt;file&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;files&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;pdf&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;PDFDocument&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;load&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;file&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;arrayBuffer&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;pages&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;merged&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;copyPages&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;pdf&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;pdf&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getPageIndices&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
  &lt;span class="nx"&gt;pages&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;merged&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addPage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;p&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;bytes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;merged&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;save&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;PDF merge in 7 lines. No server.&lt;/p&gt;

&lt;h3&gt;
  
  
  AI background removal: U²-Net via &lt;a class="mentioned-user" href="https://dev.to/imgly"&gt;@imgly&lt;/a&gt;/background-removal
&lt;/h3&gt;

&lt;p&gt;Runs an actual neural network in the browser via ONNX runtime. The model is ~80MB, downloaded once and cached.&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="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;removeBackground&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@imgly/background-removal&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;blob&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;removeBackground&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;imageURL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;publicPath&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/vendor/imgly/&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;output&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;format&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;image/png&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;quality&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;0.95&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;3 seconds on a Macbook for a typical photo. No upload of your face/photo to any "AI service."&lt;/p&gt;

&lt;h3&gt;
  
  
  AI image upscaling: Real-ESRGAN (ONNX)
&lt;/h3&gt;

&lt;p&gt;Real-ESRGAN was the SOTA upscaler. Compiled to ONNX, it runs via WebGL or WebGPU 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;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;upscaleImage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;imageBlob&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;scale&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="c1"&gt;// 1024×1024 → 4096×4096, no server, all on your GPU&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Worker isolation + SharedArrayBuffer
&lt;/h3&gt;

&lt;p&gt;The big secret to not freezing the UI on heavy operations: &lt;strong&gt;everything heavy goes in a Web Worker&lt;/strong&gt;, with &lt;code&gt;SharedArrayBuffer&lt;/code&gt; for zero-copy memory sharing.&lt;/p&gt;

&lt;p&gt;But SharedArrayBuffer requires cross-origin isolation (COEP/COOP headers). The traditional &lt;code&gt;require-corp&lt;/code&gt; mode breaks CDN scripts. The modern fix: &lt;strong&gt;&lt;code&gt;credentialless&lt;/code&gt; COEP&lt;/strong&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight toml"&gt;&lt;code&gt;&lt;span class="c"&gt;# netlify.toml&lt;/span&gt;
&lt;span class="nn"&gt;[[headers]]&lt;/span&gt;
  &lt;span class="py"&gt;for&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"/*"&lt;/span&gt;
  &lt;span class="nn"&gt;[headers.values]&lt;/span&gt;
    &lt;span class="py"&gt;Cross-Origin-Embedder-Policy&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"credentialless"&lt;/span&gt;
    &lt;span class="py"&gt;Cross-Origin-Opener-Policy&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"same-origin"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This lets FFmpeg load from a CDN, while still enabling SharedArrayBuffer. Critical for video tools.&lt;/p&gt;

&lt;h2&gt;
  
  
  The hard parts (honest list)
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Memory limits.&lt;/strong&gt; Browser tabs have ~2-4GB heap on desktop, ~500MB-1GB on mobile. A 1.5GB video on mobile = guaranteed crash. I added differentiated file size warnings: 1GB on desktop, 500MB on mobile.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;WebAssembly model downloads.&lt;/strong&gt; AI features need to download 50-200MB once. I show progress bars and cache aggressively in the browser. After the first use, the model is instant.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Browser compat.&lt;/strong&gt; SharedArrayBuffer needs Chrome/Edge/Firefox 119+. I detect this on page load and show a friendly upgrade message instead of a broken UI.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;HEIC/HEVC.&lt;/strong&gt; Apple formats. Mostly impossible to decode in vanilla browsers — I use a HEIC decoder in WASM as a fallback, but quality varies.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Internationalization.&lt;/strong&gt; Site is available in 14 languages with proper hreflang. The build script (Python) pulls translations from JSON files and injects them into 60 HTML files × 14 locales = 840 generated pages. All static, no SSR.&lt;/p&gt;

&lt;h2&gt;
  
  
  What surprised me
&lt;/h2&gt;

&lt;p&gt;The thing I didn't expect: &lt;strong&gt;most "online tools" don't actually need to be online.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Once you accept that the browser is the runtime, you stop needing a backend for most things. No server scaling, no privacy policy negotiating, no API keys management. You ship static HTML + JS + WASM and call it done.&lt;/p&gt;

&lt;p&gt;The economics are wild too. The site costs ~$0/month to run (Netlify free tier handles it). The closest paid competitor charges $10/month per user for the same set of tools.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try it / give feedback
&lt;/h2&gt;

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

&lt;p&gt;It's free, no signup. If you find a bug or have a feature idea, the contact form on the site goes to me directly.&lt;/p&gt;

&lt;p&gt;If you're a developer who'd want to dig into the implementation, I'm planning to open-source individual tools as standalone npm packages over the next few months.&lt;/p&gt;




&lt;p&gt;That's the gist. Happy to answer questions about any specific tool or technical decision in the comments.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>webassembly</category>
      <category>javascript</category>
      <category>showdev</category>
    </item>
  </channel>
</rss>
