<?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: Gordienko Roman</title>
    <description>The latest articles on Forem by Gordienko Roman (@formeo).</description>
    <link>https://forem.com/formeo</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%2F3770214%2F56de6f84-a2c2-485e-8d29-0f5cf7690f70.jpeg</url>
      <title>Forem: Gordienko Roman</title>
      <link>https://forem.com/formeo</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/formeo"/>
    <language>en</language>
    <item>
      <title>I Built an Open Source Audio Processing Ecosystem — From a Pure Go FLAC Encoder to AI Noise Removal</title>
      <dc:creator>Gordienko Roman</dc:creator>
      <pubDate>Fri, 13 Feb 2026 06:20:28 +0000</pubDate>
      <link>https://forem.com/formeo/i-built-an-open-source-audio-processing-ecosystem-from-a-pure-go-flac-encoder-to-ai-noise-removal-17l6</link>
      <guid>https://forem.com/formeo/i-built-an-open-source-audio-processing-ecosystem-from-a-pure-go-flac-encoder-to-ai-noise-removal-17l6</guid>
      <description>&lt;p&gt;I work with audio a lot — music collections, audiobooks, voice messages, podcast recordings. Every task needed a different tool, a different installation headache, and half of them required FFmpeg as a dependency.&lt;/p&gt;

&lt;p&gt;So I started building my own tools. One turned into two, two turned into six, and now it's a full ecosystem: &lt;strong&gt;&lt;a href="https://audiotools.dev/" rel="noopener noreferrer"&gt;audiotools.dev&lt;/a&gt;&lt;/strong&gt; — open source audio tools in Go and Python, plus browser-based utilities that process audio without uploading anything.&lt;/p&gt;

&lt;p&gt;Here's the technical story behind each one.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Ecosystem
&lt;/h2&gt;

&lt;p&gt;The project covers four main areas:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Audio conversion&lt;/strong&gt; — format conversion without FFmpeg (Go)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Speech transcription&lt;/strong&gt; — bulk voice-to-text with Whisper (Python)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Music identification&lt;/strong&gt; — batch Shazam recognition with auto-tagging (Python)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Audiobook cleaning&lt;/strong&gt; — AI noise/music removal with neural networks (Python)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Plus two browser-based tools on the website: an audio converter (WASM) and real-time voice-to-text (Web Speech API).&lt;/p&gt;

&lt;h2&gt;
  
  
  go-audio-converter: Why I Wrote a FLAC Encoder in Pure Go
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/formeo/go-audio-converter" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt; — this is the one I'm most proud of technically.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Problem
&lt;/h3&gt;

&lt;p&gt;Every Go audio library out there depends on either FFmpeg or CGO. That means:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Cross-compilation breaks&lt;/li&gt;
&lt;li&gt;Docker images bloat from 50MB to 500MB+&lt;/li&gt;
&lt;li&gt;WASM builds are impossible&lt;/li&gt;
&lt;li&gt;Alpine Linux needs extra packages&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I wanted a single static binary that converts audio. No dependencies. No CGO. Download and run.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Solution
&lt;/h3&gt;

&lt;p&gt;Built a converter that handles WAV, MP3, FLAC, and OGG using only pure Go libraries. The hardest part? FLAC encoding — &lt;strong&gt;no pure Go FLAC encoder existed&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;So I wrote one.&lt;/p&gt;

&lt;h3&gt;
  
  
  Writing a FLAC Encoder From Scratch
&lt;/h3&gt;

&lt;p&gt;FLAC (Free Lossless Audio Codec) compression works in stages:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Split audio into frames&lt;/strong&gt; (typically 4096 samples)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Predict&lt;/strong&gt; each sample from previous samples using a mathematical model&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Calculate residuals&lt;/strong&gt; — the difference between predicted and actual values&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Encode residuals&lt;/strong&gt; using Rice coding (a form of entropy coding)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The key insight is that predicted residuals are much smaller numbers than raw samples, so they compress well.&lt;/p&gt;

&lt;p&gt;I implemented FIXED prediction (orders 0-4), which uses polynomial prediction:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="c"&gt;// Order 0: residual = sample (no prediction)&lt;/span&gt;
&lt;span class="c"&gt;// Order 1: residual = sample - prev1&lt;/span&gt;
&lt;span class="c"&gt;// Order 2: residual = sample - 2*prev1 + prev2&lt;/span&gt;
&lt;span class="c"&gt;// Order 3: residual = sample - 3*prev1 + 3*prev2 - prev3&lt;/span&gt;
&lt;span class="c"&gt;// Order 4: residual = sample - 4*prev1 + 6*prev2 - 4*prev3 + prev4&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The encoder tries all five orders and picks the one that produces the smallest residuals for each frame. Then it encodes them using Rice coding, where a parameter &lt;code&gt;k&lt;/code&gt; determines how many bits to use for the quotient vs remainder:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;riceEncode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;residual&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;k&lt;/span&gt; &lt;span class="kt"&gt;uint&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c"&gt;// Map signed to unsigned (zig-zag encoding)&lt;/span&gt;
    &lt;span class="n"&gt;unsigned&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;zigZag&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;residual&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;quotient&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;unsigned&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;k&lt;/span&gt;
    &lt;span class="n"&gt;remainder&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;unsigned&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="c"&gt;// Write quotient as unary (q ones + zero)&lt;/span&gt;
    &lt;span class="c"&gt;// Write remainder as k bits&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The Rice parameter &lt;code&gt;k&lt;/code&gt; is chosen per partition by minimizing the total encoded bit length.&lt;/p&gt;

&lt;p&gt;Compression results:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Content&lt;/th&gt;
&lt;th&gt;Ratio&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Silence&lt;/td&gt;
&lt;td&gt;95%+&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Sine wave&lt;/td&gt;
&lt;td&gt;50-70%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Music&lt;/td&gt;
&lt;td&gt;30-50%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;White noise&lt;/td&gt;
&lt;td&gt;~0%&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;It's not as optimal as libFLAC (which uses LPC prediction), but it's the first pure Go FLAC encoder and it works everywhere — including WASM in the browser.&lt;/p&gt;

&lt;h3&gt;
  
  
  The WASM Angle
&lt;/h3&gt;

&lt;p&gt;Because everything is pure Go, I compiled the converter to WebAssembly and embedded it in the &lt;a href="https://audiotools.dev/converter/" rel="noopener noreferrer"&gt;audiotools.dev/converter&lt;/a&gt; page. You drop an audio file in the browser, it converts locally using the Go code compiled to WASM. No upload, no server.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;GOOS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;js &lt;span class="nv"&gt;GOARCH&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;wasm go build &lt;span class="nt"&gt;-o&lt;/span&gt; converter.wasm ./cmd/wasm
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The WASM binary is ~4MB — larger than I'd like, but acceptable for a tool that replaces a server-side FFmpeg pipeline.&lt;/p&gt;

&lt;h2&gt;
  
  
  voice-to-text: Bulk Transcription Pipeline
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/formeo/voice-to-text" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt; — Python tool for transcribing voice messages in bulk.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Problem I Was Solving
&lt;/h3&gt;

&lt;p&gt;I had hundreds of Telegram voice messages that I needed as text. Manual transcription was out of the question. Existing tools handle one file at a time.&lt;/p&gt;

&lt;h3&gt;
  
  
  Multiple Backends
&lt;/h3&gt;

&lt;p&gt;The tool supports four transcription backends:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Whisper&lt;/strong&gt; (local) — OpenAI's model running locally. Best quality, needs GPU.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;faster-whisper&lt;/strong&gt; — CTranslate2-optimized Whisper. 4x faster, same quality.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;OpenAI API&lt;/strong&gt; — cloud Whisper. No GPU needed, costs money.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Groq API&lt;/strong&gt; — fastest cloud option. Free tier available.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It also includes a Telegram bot that transcribes voice messages in real-time — send a voice note, get text back.&lt;/p&gt;

&lt;h3&gt;
  
  
  Browser Version
&lt;/h3&gt;

&lt;p&gt;The website has a &lt;a href="https://audiotools.dev/voice-to-text/" rel="noopener noreferrer"&gt;voice-to-text page&lt;/a&gt; that uses the Web Speech API for real-time transcription in 60+ languages. No AI model downloads, no processing — the browser handles everything natively.&lt;/p&gt;

&lt;p&gt;The Web Speech API is underrated. It's built into Chrome, Edge, and Safari, works in real-time, and supports dozens of languages. The catch? It requires an internet connection (audio goes to Google's/Apple's servers for recognition). But for quick transcription, it's instant and free.&lt;/p&gt;

&lt;h2&gt;
  
  
  music_recognition: Batch Shazam
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/formeo/music_recognition" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt; — identify unknown music files using Shazam's algorithm.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Backstory
&lt;/h3&gt;

&lt;p&gt;I had a folder with 2,000+ MP3 files from the early 2000s. Filenames like &lt;code&gt;Track_001.mp3&lt;/code&gt;, &lt;code&gt;Unknown Artist - Unknown Track.mp3&lt;/code&gt;, &lt;code&gt;(3).mp3&lt;/code&gt;. No metadata, no tags.&lt;/p&gt;

&lt;p&gt;This tool scans a directory, identifies each track via Shazam, and automatically:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Writes ID3 tags (artist, title, album, year)&lt;/li&gt;
&lt;li&gt;Renames files to &lt;code&gt;Artist - Title.mp3&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Organizes into &lt;code&gt;Artist/Album/&lt;/code&gt; folder structure&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It uses the &lt;a href="https://github.com/shazamio/ShazamIO" rel="noopener noreferrer"&gt;ShazamIO&lt;/a&gt; library, which reverse-engineered Shazam's recognition API. Rate limiting and retries are built in — Shazam will throttle you if you hit it too fast.&lt;/p&gt;

&lt;h2&gt;
  
  
  Audiobook-Cleaner: AI Noise Removal
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/formeo/Audiobook-Cleaner" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt; — clean audiobooks from background music, noise, and echo using neural networks.&lt;/p&gt;

&lt;h3&gt;
  
  
  How It Works
&lt;/h3&gt;

&lt;p&gt;The tool is a wrapper over &lt;a href="https://github.com/nomadkaraoke/python-audio-separator" rel="noopener noreferrer"&gt;audio-separator&lt;/a&gt;, which implements the same AI models used by Ultimate Vocal Remover (UVR5):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;MDX-Net&lt;/strong&gt; — best for music/voice separation&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;VR Architecture&lt;/strong&gt; — good for noise removal&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Roformer&lt;/strong&gt; — state-of-the-art transformer model&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The key problem I solved: &lt;strong&gt;large file handling&lt;/strong&gt;. These models expect short audio segments (typically 30-60 seconds). A 10-hour audiobook crashes any of them. My wrapper automatically chunks files, processes each segment, and stitches results back together — handling crossfades at chunk boundaries to avoid clicks.&lt;/p&gt;

&lt;p&gt;Works on both CPU and GPU (NVIDIA CUDA). CPU processing is viable for a single audiobook; for batch processing, GPU is 10-20x faster.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I Learned Building This
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Go for CLI tools, Python for ML pipelines
&lt;/h3&gt;

&lt;p&gt;This split worked perfectly. Go gives you:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Single binary distribution (no runtime, no pip install)&lt;/li&gt;
&lt;li&gt;Easy cross-compilation&lt;/li&gt;
&lt;li&gt;WASM compilation for browser deployment&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Python gives you:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Access to ML models and the entire AI ecosystem&lt;/li&gt;
&lt;li&gt;Rapid prototyping with libraries like faster-whisper, audio-separator&lt;/li&gt;
&lt;li&gt;Jupyter notebook experimentation&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  WASM is viable for real tools
&lt;/h3&gt;

&lt;p&gt;The audio converter running in the browser via WASM handles real-world files. It's not a toy demo. Users drop 50MB FLAC files and get MP3s back without any upload. The 4MB WASM binary is a fair trade for zero server costs.&lt;/p&gt;

&lt;h3&gt;
  
  
  Privacy is a feature
&lt;/h3&gt;

&lt;p&gt;Every browser-based tool on audiotools.dev processes data locally. This isn't just a philosophical choice — it eliminates server costs, simplifies architecture, and genuinely matters to users processing personal audio (voice messages, private recordings).&lt;/p&gt;

&lt;h2&gt;
  
  
  Try It
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Website:&lt;/strong&gt; &lt;a href="https://audiotools.dev/" rel="noopener noreferrer"&gt;audiotools.dev&lt;/a&gt; — browser-based converter and voice-to-text&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;GitHub:&lt;/strong&gt; &lt;a href="https://github.com/formeo" rel="noopener noreferrer"&gt;github.com/formeo&lt;/a&gt; — all projects, MIT licensed&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;go-audio-converter:&lt;/strong&gt; &lt;code&gt;go install github.com/formeo/go-audio-converter/cmd/audioconv@latest&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Everything is open source under MIT. Contributions, issues, and stars welcome.&lt;/p&gt;

&lt;p&gt;I'd love to hear: what audio processing tasks do you struggle with? What would you add to the ecosystem?&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Built with Go and Python. The browser tools use WASM and Web Speech API. No FFmpeg was harmed in the making of this ecosystem.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>go</category>
      <category>python</category>
      <category>opensource</category>
      <category>audio</category>
    </item>
    <item>
      <title>I Built 19 Free Developer Tools That Run Entirely in the Browser — Here's What I Learned</title>
      <dc:creator>Gordienko Roman</dc:creator>
      <pubDate>Fri, 13 Feb 2026 06:13:06 +0000</pubDate>
      <link>https://forem.com/formeo/i-built-19-free-developer-tools-that-run-entirely-in-the-browser-heres-what-i-learned-249h</link>
      <guid>https://forem.com/formeo/i-built-19-free-developer-tools-that-run-entirely-in-the-browser-heres-what-i-learned-249h</guid>
      <description>&lt;p&gt;I got tired of jumping between 5-6 different sites just to format JSON, decode a JWT, or generate a UUID. Each one had signup popups, cookie banners, and ads covering half the screen.&lt;/p&gt;

&lt;p&gt;So I built &lt;strong&gt;&lt;a href="https://www.devtoolkit.site/" rel="noopener noreferrer"&gt;DevToolKit&lt;/a&gt;&lt;/strong&gt; — a collection of 19 browser-based developer tools. No backend, no signup, no data ever leaves your machine.&lt;/p&gt;

&lt;p&gt;Here's what I learned building it, the technical decisions that mattered, and what surprised me about SEO for tool sites.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Stack
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Next.js 14&lt;/strong&gt; with App Router — each tool is a separate route with its own metadata&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Tailwind CSS&lt;/strong&gt; — dark theme, consistent design across all tools&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Zero backend&lt;/strong&gt; — everything runs client-side with native browser APIs&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Vercel&lt;/strong&gt; for hosting — automatic deployments from GitHub&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;No database. No authentication. No server-side processing. Every tool is a self-contained React component that processes data locally.&lt;/p&gt;

&lt;h2&gt;
  
  
  The 19 Tools
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Data:&lt;/strong&gt; JSON Formatter &amp;amp; Validator, JSON Tree Viewer (collapsible tree with path copying), YAML ↔ JSON Converter, SQL Formatter&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Encoding:&lt;/strong&gt; Base64 Encoder/Decoder (text + file drag &amp;amp; drop), URL Encoder/Decoder&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Security:&lt;/strong&gt; JWT Decoder, Hash Generator (SHA-1/256/384/512 via Web Crypto API), Password Generator (crypto.getRandomValues())&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;DevOps:&lt;/strong&gt; Cron Expression Parser (human-readable descriptions + next run times), PostgreSQL Config Generator (a PGTune alternative)&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Generators:&lt;/strong&gt; UUID v4, QR Code (PNG + SVG), Lorem Ipsum&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Text &amp;amp; Reference:&lt;/strong&gt; Regex Tester, Text Diff Checker, Unix Timestamp Converter, Color Converter (HEX/RGB/HSL), HTTP Status Codes Reference&lt;/p&gt;

&lt;h2&gt;
  
  
  Technical Decisions That Mattered
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Each tool = separate page with its own SEO
&lt;/h3&gt;

&lt;p&gt;Instead of building a single-page app with tab switching, every tool has its own URL, title, meta description, and canonical tag. This means Google indexes each tool independently:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/json-formatter/   → "JSON Formatter &amp;amp; Validator"
/cron-parser/      → "Cron Expression Parser &amp;amp; Generator"
/postgres-config/  → "PostgreSQL Config Generator"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is huge for organic traffic. Someone googling "cron parser online" lands directly on the cron tool, not a generic homepage.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Web Crypto API over external libraries
&lt;/h3&gt;

&lt;p&gt;For hashing and password generation, I use the native Web Crypto API (&lt;code&gt;crypto.subtle.digest()&lt;/code&gt; and &lt;code&gt;crypto.getRandomValues()&lt;/code&gt;) instead of importing crypto-js or similar libraries. Benefits:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Zero bundle size for crypto operations&lt;/li&gt;
&lt;li&gt;Hardware-accelerated on most devices&lt;/li&gt;
&lt;li&gt;Same security guarantees as server-side implementations
&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;const&lt;/span&gt; &lt;span class="nx"&gt;hash&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;crypto&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;subtle&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;digest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;SHA-256&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;TextEncoder&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;encode&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3. FileReader API for Base64 file encoding
&lt;/h3&gt;

&lt;p&gt;The Base64 tool supports drag &amp;amp; drop file encoding without uploading anything. The FileReader API reads the file locally and converts it to a data URI:&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;reader&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;FileReader&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nx"&gt;reader&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;onload&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;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;base64&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;reader&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;result&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;,&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
  &lt;span class="c1"&gt;// Ready to use — never left the browser&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="nx"&gt;reader&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;readAsDataURL&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;file&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is the killer feature users don't expect — drop an image, get a &lt;code&gt;data:image/png;base64,...&lt;/code&gt; string instantly.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. PostgreSQL config calculations
&lt;/h3&gt;

&lt;p&gt;The PG Config tool implements the same tuning algorithms as PGTune. The key formula for &lt;code&gt;work_mem&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;work_mem = (RAM - shared_buffers) / (max_connections × 3)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;×3&lt;/code&gt; factor accounts for the fact that a single query can use multiple work_mem allocations (sorts, hash joins, etc.). For data warehouse workloads, we use &lt;code&gt;×1&lt;/code&gt; since there are fewer concurrent connections.&lt;/p&gt;

&lt;h3&gt;
  
  
  5. Cron parser with actual execution time calculation
&lt;/h3&gt;

&lt;p&gt;Instead of just describing the cron expression, the parser calculates the next 10 actual execution times by iterating through minutes:&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;d&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;Date&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;525960&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;runs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;10&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="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;months&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;includes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;d&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getMonth&lt;/span&gt;&lt;span class="p"&gt;()&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="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;
      &lt;span class="nx"&gt;doms&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;includes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;d&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getDate&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;
      &lt;span class="nx"&gt;dows&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;includes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;d&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getDay&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;
      &lt;span class="nx"&gt;hours&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;includes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;d&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getHours&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;
      &lt;span class="nx"&gt;mins&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;includes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;d&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getMinutes&lt;/span&gt;&lt;span class="p"&gt;()))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;runs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&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;Date&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;d&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="nx"&gt;d&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setMinutes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;d&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getMinutes&lt;/span&gt;&lt;span class="p"&gt;()&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="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Simple brute force, but 525,960 iterations (one year of minutes) runs in &amp;lt;10ms on any modern device.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I Learned About SEO for Tool Sites
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Every page needs text content
&lt;/h3&gt;

&lt;p&gt;Initially, most tool pages were just UI — an input, a button, an output. Google ranked them nowhere. Adding 2-3 paragraphs of genuine "about this tool" content to each page explaining what it does, why it exists, and how it works — rankings started climbing within 2 weeks.&lt;/p&gt;

&lt;h3&gt;
  
  
  Meta descriptions matter more than I expected
&lt;/h3&gt;

&lt;p&gt;Pages with 40-50 character descriptions were getting ignored or Google was generating its own (usually bad) snippets. Expanding every description to 120-180 characters with action keywords made a visible difference in click-through rates.&lt;/p&gt;

&lt;h3&gt;
  
  
  Domain consistency is critical
&lt;/h3&gt;

&lt;p&gt;I had my sitemap pointing to &lt;code&gt;www.devtoolkit.site&lt;/code&gt; but canonical tags pointing to &lt;code&gt;devtoolkit.site&lt;/code&gt; (without www). Google was treating them as two different sites and splitting my authority. One-line fix, significant impact.&lt;/p&gt;

&lt;h3&gt;
  
  
  Mobile-first is not optional
&lt;/h3&gt;

&lt;p&gt;My original sidebar navigation ate 250px on mobile screens, leaving tools nearly unusable on phones. Adding a responsive hamburger menu and adjusting padding increased mobile engagement immediately. Google notices when users bounce from mobile — it affects rankings.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's Next
&lt;/h2&gt;

&lt;p&gt;I'm planning to add:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;CSV ↔ JSON Converter&lt;/li&gt;
&lt;li&gt;chmod Calculator&lt;/li&gt;
&lt;li&gt;Markdown Preview&lt;/li&gt;
&lt;li&gt;JWT Generator (not just decoder)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Try It
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://www.devtoolkit.site/" rel="noopener noreferrer"&gt;https://www.devtoolkit.site/&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;All 19 tools, zero signup, zero tracking. Everything runs in your browser tab.&lt;/p&gt;

&lt;p&gt;I'd love to hear what tools you use daily that are missing, or any feedback on the UX. Drop a comment or open an issue on GitHub.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Built with Next.js 14, Tailwind CSS, and deployed on Vercel. No backend, no database, no cookies.&lt;/em&gt;&lt;/p&gt;

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