惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

H
Heimdal Security Blog
P
Privacy International News Feed
S
Schneier on Security
P
Proofpoint News Feed
L
Lohrmann on Cybersecurity
Spread Privacy
Spread Privacy
P
Privacy & Cybersecurity Law Blog
D
Darknet – Hacking Tools, Hacker News & Cyber Security
Scott Helme
Scott Helme
K
Kaspersky official blog
大猫的无限游戏
大猫的无限游戏
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
aimingoo的专栏
aimingoo的专栏
Simon Willison's Weblog
Simon Willison's Weblog
S
Securelist
Help Net Security
Help Net Security
B
Blog
H
Hackread – Cybersecurity News, Data Breaches, AI and More
Security Archives - TechRepublic
Security Archives - TechRepublic
云风的 BLOG
云风的 BLOG
The GitHub Blog
The GitHub Blog
N
News and Events Feed by Topic
Hacker News: Ask HN
Hacker News: Ask HN
cs.CV updates on arXiv.org
cs.CV updates on arXiv.org
CTFtime.org: upcoming CTF events
CTFtime.org: upcoming CTF events
M
MIT News - Artificial intelligence
雷峰网
雷峰网
博客园 - 司徒正美
V
V2EX
AWS News Blog
AWS News Blog
Know Your Adversary
Know Your Adversary
N
News | PayPal Newsroom
T
Tor Project blog
Cisco Talos Blog
Cisco Talos Blog
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
PCI Perspectives
PCI Perspectives
Google DeepMind News
Google DeepMind News
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
U
Unit 42
C
Cybersecurity and Infrastructure Security Agency CISA
P
Palo Alto Networks Blog
G
Google Developers Blog
T
Threat Research - Cisco Blogs
博客园 - Franky
I
InfoQ
D
DataBreaches.Net
爱范儿
爱范儿
Y
Y Combinator Blog
博客园 - 叶小钗
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报

DEV Community

Authentication Security Deep Dive: From Brute Force to Salted Hashing (With Java Examples) Why AI Systems Don’t Fail — They Drift Spilling beans for how i learn for exam😁"Reinforcement Learning Cheat Sheet" I Replaced Chrome with Safari for AI Browser Automation. Here's What Broke (and What Finally Worked) How Python Borrows Other People's Work The $40 Architecture: Processing 1 Billion API Requests with 99.99% Uptime Vibe Coding: A Workflow Guide (From Zero to SaaS) Most webhook security guides protect the wrong side. The scary part is delivery. Headless CMS for TanStack Start: Build a Blog with Cosmic EU Age Verification App "Hacked in 2 Minutes" — What Actually Happened Comfy Cloud’s delete function does not actually remove files Running AI Models on GPU Cloud Servers: A Beginner Guide Event-driven media intelligence with AWS Step Functions and Bedrock I scored 500 AI prompts across 8 quality dimensions — here's what broke How to Call Google Gemini API from Next.js (Free Tier, No Backend Needed) The Portal Protocol: Reclaiming Human Connection in the Age of AI How to Fix Your Team's Scattered Knowledge Problem With a Self-Hosted Forum Intro to tc Cloud Functors: A Graph-First Mental Model for the Modern Cloud Designing Multi-Tenant Backends With Both Ownership and Team Access I Built a Neumorphic CSS Library with 77+ Components — Here's What I Learned PostgreSQL Performance Optimization: Why Connection Pooling Is Critical at Scale Cómo construí un SaaS multi-rubro para gestionar expensas en Argentina con FastAPI + Vue 3 🚀 I Built an Ethical Hacking Scanner Tool – Open Source Project I Replaced /usage and /context in Claude Code With a Single Statusline A Pythonic Way to Handle Emails (IMAP/SMTP) with Auto-Discovery and AI-Ready Design I Collected 8.9 Million Polymarket Price Points — Here's What I Found About How Markets Really Move EcoTrack AI — Carbon Footprint Tracker & Dashboard Everyone's Using AI. No One Agrees How. 5 self-hosted ebook managers worth trying in 2026 Building Your First AI Agent with LangChain: From Chatbot to Autonomous Assistant Common SOC 2 Failures (Real World) Stop Vibe-Checking Your AI App: A Practical Guide to Evals How to Use SonarQube and SonarScanner Locally to Level Up Your Code Quality Your Next To-Do App Is Dead — I Replaced Mine with an OpenClaw AI Sign a Nostr event in 60 lines of Python using coincurve — no nostr-sdk, no nbxplorer, no rust toolchain ITGC Audit Explained Like You’re in Big 4 Patch Tuesday abril 2026: Microsoft parcha 163 vulnerabilidades y un zero-day en SharePoint Stop scraping everything: a better way to track competitor price changes Listing on MCPize + the Official MCP Registry while routing payments OUTSIDE the marketplace — how I kept 100% of my x402 revenue Building an AI-Powered Risk Intelligence System Using Serverless Architecture Why We Ripped Function Overloading Out of Our AI Toolchain Testing AI-Generated Code: How to Actually Know If It Works SaaS Churn Is Killing Your Business. Here Is What to Do About It (Without a Support Team) The Speed of AI Is No Longer Linear - And Self-Improving Models Are Why How to Implement RBAC for MCP Tools: A Practical Guide for Engineering Teams From Standard Quote to Persuasive Proposal: AI Automation for Arborists I built a CLI that scaffolds complete multi-tenant SaaS apps Axios CVE-2025–62718: The Silent SSRF Bug That Could Be Hiding in Your Node.js App Right Now The dashboard that ended our friendship Data Pipelines Explained Simply (and How to Build Them with Python) The Hidden Cost of AI Systems Nobody Talks About. undefined vs undeclared, and how typeof behaves Switching from file-based jobs to NATS/Kafka in Rust without changing code io_uring Adventures: Rust Servers That Love Syscalls Why Agentic AI is Killing the Traditional Database The POUR principles of web accessibility for developers and designers Quantum Neural Network 3D — A Deep Dive into Interactive WebGL Visualization How To Install Caveman In Codex On macOS And Windows Automation Pipeline Reliability: Why Your Workflow Breaks When Nobody Is Watching I Built an 'Open World' AI Coding Agent — It Works From ANY Folder From Freelancing to Product: A Tech Service Company's SaaS Transformation China's AI Giants: Adding Tencent Hunyuan & ByteDance Doubao to AI University (74 Providers) On the Vibe Coders and Their Lies clerk: Auto-Summarize Your Claude Code Sessions AI Weekly — 2026/04/10–04/17 | The Model Lockdown Is Here, but the Toolchain Is the Real Battleground AI 週報 — 2026/04/10–2026/04/17 模型封鎖潮來了,但工具鏈才是真戰場 Maybe this is how Open-Source apps are born... 🚀 Fine-Tune LLMs with LoRA and QLoRA: 2026 Guide tRPC v11 + Next.js App Router: End-to-End Type Safety Without the Boilerplate ShadCN UI in 2026: Why I Stopped Installing Component Libraries and Started Owning My Components SaaS Billing in React Server Components: Stripe + Supabase Without a Single `useEffect` Join our DEV Weekend Challenge — $1,000 in Prizes Across TEN winners! Submissions Due April 20 at 6:59 AM UTC. Implementing FSRS Spaced Repetition in Flutter + Supabase — Adding Memory Science to an AI Learning App "I Texted My Localhost From the Train — Claude Code Fixed the Bug Before I Got Home" I Built a Sales Prep AI and It Went Deeper Than Expected Design to Code #2: One JSON, Eleven Outputs Solving the 100M-Row Problem: A Summary Table Pattern for High-Volume Push Notification Logs Flutter Web With Wasm: What Actually Changes For Developers I Built 50 Royalty-Free Soundtracks for My Side Project in a Weekend Using AI Music Generation The Vibe Coding Security Checklist: 7 Things to Check Before You Ship Stop Letting Googlebot Guess Fix Your React App's SEO Right Desconstruindo o Streaming do LinkedIn: Como Criar um Engine de Extração de Vídeo de Alta Performance com HLS e FFmpeg (EDA Part-1) EDA (Exploratory Data Analysis) Explained With Real Life — Why Looking at Your Data Is the Most Important Step in Machine Learning Brand Relationship Management at Scale: Our 4-Touch Outreach System for 200+ Brands Why String.fromEnvironment() Might Return an Empty String in Dart JGuardrails 1.0.0 — Hardening Java LLM Apps Against Jailbreaks, Toxicity, and Prompt Injection Plan and Schedule a Full Week of Threads Content From One Claude Conversation Coding Cat Oran Ep3, Five Tables Changed Everything Updated: BFF Pattern I'm done watching freelancers get buried by 200 proposals. So I'm building the alternative. This is my first post BFS Algorithm in Java Step by Step Tutorial with Examples Tracking LLM Pricing Monthly: An Open Dataset for 22 AI Models How We Measure Content ROI on a Comparison Site: Revenue Attribution Without Perfect Data Introducing Nova AI Ops: The AI-Native Operating System for SRE Teams I built a free desktop video downloader for Windows — Grabbit How Talkie OCR Helps Vision-Impaired & Dyslexic Users Read the World Around Them VRCFaceTracking安装和iPhone面捕配置教程,有bug Even CrowdStrike Can't See Your Agents The Automation Gold Rush: What n8n Workflows and Claude Are Opening Up for Developers Right Now
I Spent $200 in One Day on AI Tokens to Build 86 Browser Tools — A Solo Founder's Complete Build Log
Zihang Dong 董子航 · 2026-06-13 · via DEV Community

86 tools. Zero backend. One person. Two hospital visits. $200 in tokens in 24 hours. This is what building in public actually looks like — not the highlight reel, but the raw commit history of a human being.


The Genesis: Why Would Anyone Build 86 Free Tools?

On March 18, 2026, I launched ToolKnit — a collection of free, browser-based utilities that require no signup, no uploads, and zero trust.

Today, it's 86 tools across 10 categories. PDF converters that run entirely client-side using PDF-lib.js. Image resizers powered by HTML5 Canvas. A Morse Code Translator with audible beep playback at adjustable WPM. A MIDI Keyboard with 8 synthesized timbres — Grand Piano, Electric Organ, Synth Lead, String Ensemble, Pipa, Guitar, Flute, and Music Box — all generated in real-time through the Web Audio API. A Lyric Visualizer that parses LRC files and syncs lyrics to audio waveforms. Even a drinking-game dare spinner with 300 dares across 6 categories, slot-machine text reveal animation, and confetti celebrations.

But here's what the landing page doesn't tell you: I just spent $200 in a single day on Claude Max tokens to migrate a handful of pages to a new bilingual architecture. I killed a feature after 48 hours of intensive debugging. And somewhere between the ffmpeg.wasm Worker CORS errors and the hospital visit, I learned that some bugs aren't in the code at all.

This isn't a success story. It's a build log.


Part I: The Stack — Why I Chose Boring Technology

Before I tell you about the features that lived and died, you need to understand the foundation. ToolKnit is pure static HTML + Tailwind CSS + Vanilla JS. No React. No Vue. No Next.js. No build step beyond Tailwind compilation.

Why?

  • SEO-first: Static HTML means Google sees everything immediately. No hydration delays. No client-side routing. Every tool page is a complete, crawlable document with full Open Graph tags, Twitter Cards, and structured data.
  • Zero JS framework bloat: Most tools work without JavaScript. Progressive enhancement is the default, not an afterthought.
  • Server costs near zero: The only PHP endpoints are track.php (stats) and public-stats.php (aggregated metrics). All image processing, PDF manipulation, audio synthesis, and video transcoding happens in the browser.
  • Deployment is scp: One file, one command, zero CI/CD complexity. The Service Worker (toolknit-v119 as of today) handles offline caching for returning visitors.

The tradeoff? Manual consistency across 86 pages. When you add a new footer link, you're editing 86 HTML files (or writing a Python script to do it, then verifying each one). That's why the bilingual architecture had to be a shared module, not a framework.

The PWA layer deserves its own mention. manifest.json declares the site as installable. The Service Worker precaches core assets. Users on slow connections get instant loads after the first visit. For a site with 86 tool pages, this matters more than any SPA transition animation.


Part II: The Tools That Lived — Engineering Notes from the Trenches

The MIDI Keyboard: 8 Timbres, 1 Web Audio Context

Building a browser-based MIDI keyboard sounds straightforward until you realize that "piano sound" isn't a single oscillator. Each timbre required distinct synthesis architecture:

Pipa (琵琶): Sawtooth + square wave generators with non-integer harmonics (5.04×, 7.01× fundamental) to simulate the metallic twang of plucked silk strings. A high-Q resonant lowpass filter shapes the body resonance, and a noise burst layer provides the finger-pick attack transient. Without that noise burst, it sounds like a cheap synth preset.

Guitar: Triangle wave fundamental + 6 overtone harmonics with body resonance simulation via 105 Hz and 215 Hz bandpass filters (acoustic cavity modes). The pluck noise layer is subtler than Pipa but essential — without it, notes sound like a keyboard patch, not a string.

Flute: Pure sine wave with faint 2nd harmonic, vibrato LFO at 5.5 Hz (8 cents depth, delayed onset at 0.3s to avoid startup wobble), and breath noise via highpass-filtered white noise at 3 kHz. The breath noise is what separates a "flute-like" synth from an actual flute.

Music Box: Non-integer bell partials (2.756×, 5.404×, 8.933× fundamental) for the metallic chime character, octave-above overtone, and a 400 Hz highpass filter to remove low-end rumble. Long natural decay — no ADSR envelope can fake this without custom release curves.

I also added a Quick Play Songs panel with Für Elise, Summer (Kikujiro), Ode to Joy, Canon in D, Castle in the Sky, and Always With Me. The keyboard auto-shifts octaves as you follow the key sequences. When I first tested it by playing Für Elise with the Z key, I couldn't stop grinning.

The Morse Code Translator: Audio, Flash, and International Alphabet

The Morse Code Translator was the first tool built entirely on the new bilingual blueprint. Two-way text↔Morse with adjustable WPM (5–40), audible beep playback via Web Audio API oscillator nodes, screen-flash signaling for accessibility, and the full international Morse alphabet (including accented characters and Prosigns like SOS, AR, SK).

The audio timing follows strict ITU-R standards: a "dit" is one time unit, a "dah" is three units, intra-character spacing is one unit, inter-character spacing is three units, and inter-word spacing is seven units. Getting the rhythm wrong makes Morse feel "mechanical" rather than "musical." The gap between characters matters as much as the beep itself.

The Lyric Visualizer: Web Audio API + Canvas + LRC Parsing

Shipped the same day I killed Live Photo Frame. Upload an MP3/WAV + LRC lyrics file for a full-screen immersive music player with real-time audio waveform visualization, time-synced fading lyrics, draggable progress bar, automatic ID3 cover art extraction, and a dark/light theme toggle.

The Web Audio API analyzer node pulls frequency data for the waveform canvas. LRC timestamp parsing converts [mm:ss.xx] markers into millisecond anchors. Lyrics fade in/out based on proximity to the current playback position. All in the browser. No server ever sees your audio files.

Spin the Dare: From Canvas Wheel to Slot Machine

The original Spin the Dare used a canvas roulette wheel. I rebuilt it with a slot-machine-style text reveal animation — the dare scrambles through random prompts rapidly, then eases to a dramatic stop with sound cues (scale transitions, pitch-shifted chimes) and confetti explosions.

Why replace a working wheel? Because the wheel had 300 segments at small radii — visually illegible. The slot-machine approach keeps every dare readable at any screen size, adds suspense through the scramble phase, and uses audio cues (pitch rising during scramble, resolving chord at stop) to create a more theatrical experience.


Part III: The Features That Died — When Technical Soundness Isn't Enough

Live Photo Frame: 48 Hours, 3 Fatal Flaws

The idea was elegant: frame iPhone Live Photos (HEIC + MOV pairs) and Android Motion Photos (JPG with embedded MP4) in 9:16 or 16:9 layouts with large rounded corners, black or white backgrounds, and export to static frame or video.

What I built:

  • Client-side HEIC-to-JPEG conversion via heic2any
  • ffmpeg.wasm (single-thread, ~32 MB one-time download) for H.265 MOV transcoding and MP4 encoding
  • Auto-detection of iPhone Live Photo pairs, Android Motion Photos, and standalone video files
  • Canvas rendering with configurable corner radii

Why I killed it after 48 hours:

  1. Canvas quadraticCurveTo draws parabolas, not circular arcs. At radii above ~60px, "rounded corners" morph into chamfered edges. The visual signature of the tool — those elegant large-radius corners — was mathematically impossible with the Canvas API. You need arc() with proper tangent calculations, not quadraticCurveTo().

  2. HEVC/H.265 videos from Android Motion Photos won't play natively in Chrome. ffmpeg.wasm transcoding at 1080×1920 resolution was agonizingly slow — 30+ seconds for a 3-second clip on a MacBook Pro. Users don't wait 30 seconds.

  3. WeChat and messaging apps strip embedded video data from Motion Photos. The primary sharing pipeline (user receives Motion Photo via WeChat → opens in ToolKnit) was destroyed by app-level data stripping. The tool's core use case didn't exist in the real world.

Three strikes: visual quality, performance, and reliability. No amount of patching fixes a fundamentally flawed concept for the browser platform.

"A tool that 'almost works' is worse than no tool at all — it erodes trust. Ship what you're proud of, not what merely functions."

AI Pixel Art GIF: Twice Bitten

First attempt (June 9): Used DeepSeek API to generate pixel art from text prompts. Quality was garbage — LLMs don't understand pixel-level constraints.

Second attempt: Replaced DeepSeek with local Canvas pixelation (drawImage at reduced size, then drawImage back up with imageSmoothingEnabled = false) + gif.js encoding + local color analysis for smart animation selection. Fixed Worker CORS by hosting gif.worker.js locally. Fixed Canvas willReadFrequently warnings. Replaced DeepSeek API 400 errors with local analysis.

All solid engineering fixes. The tool "worked." But the output wasn't compelling. Pixel art GIFs demand either perfect algorithmic control or genuine artistic AI — we had neither.


Part IV: The $200 Day — Rebuilding 19 Pages in Bilingual Architecture

The Problem

Every tool page was English-only. Hardcoded strings everywhere. No SEO structure beyond basic meta tags. Footer layouts inconsistent across 80+ pages. For a site serving both English and Chinese speakers, this was a growth ceiling.

The Architecture

I designed a two-layer bilingual system:

Layer File Scope
Shared UI bilingual.js Nav, footer, breadcrumb, related tools, modals, feature pills
Page-specific [tool].js Hero title/desc, tool UI labels, How It Works, Why Use, Pro Tips, FAQ
Translations locales/zh.json Tool card titles/descriptions for Related Tools
Schemas Inline JSON-LD SoftwareApplication + FAQPage + BreadcrumbList per page

Key decisions:

  • English stays the default. lang="en" is static. SEO is entirely English-first. Chinese is a UX enhancement that persists across sessions via localStorage.
  • Zero impact on legacy pages. bilingual.js only activates on pages that explicitly reference it. Old pages continue working exactly as before.
  • Structured data is mandatory. Every bilingual page ships with FAQPage JSON-LD that matches the visible FAQ accordion content exactly — question-for-question, answer-for-answer.
  • Cross-page language memory. Switch to Chinese on the Image Resizer, visit the PDF Compressor, it remembers your preference.

The Cost

Migrating 5 PDF tools + Spin the Dare + Morse Code Translator + Mic & Camera Test + 11 image converters burned through $100+ in AI tokens. The full June 12 session — which included writing the bilingual.js module, creating the template, and migrating the first batch — hit $200.

The architecture itself is genuinely beautiful. But every refinement comes with a price tag measured in dollars per hour. The remaining 67 legacy pages will migrate gradually. It isn't laziness; it's arithmetic.


Part V: The Migration War — 11 Image Tools, 11 Ways to Break

Over the past 72 hours, I migrated 11 image tools: Image to PDF, Compress Image, JPG to PNG, PNG to JPG, JPG to WebP, PNG to WebP, WebP to JPG, WebP to PNG, Image Grid Split, Image Crop, and Image Resizer.

Each migration follows the same pattern — and produces its own unique bug.

Bug 1: The Single Quote That Killed a Language Switcher

// Inside RESIZER_I18N.en:
why2Desc: 'Since everything runs on your machine, there's no upload delay...'
//                                                  ^ unescaped single quote

This unescaped ' terminated the string early. The JS parser hit s no upload delay... and threw SyntaxError: Unexpected identifier. Every function defined after this line — including window.switchLang — was never created.

User-facing result: Clicking the EN/中文 language toggle produced window.switchLang is not a function. The entire bilingual system failed silently.

Fix: there'sthere\'s. Now I validate every i18n dictionary with node -e "$(cat file.html | sed -n '/RESIZER_I18N/,/^ };/p')" before deploying.

Bug 2: The Batch Script That Silently Missed Two Tips

I wrote Python scripts to auto-inject data-i18n attributes into HTML. The replacement logic:

('Use JPG at 80-85% quality for photos', ' data-i18n="tip2">Use JPG at 80-85% quality for photos')

But the actual HTML contained — (HTML entity) in some places and (Unicode em-dash) in others. The string match failed silently. Two out of four Pro Tips never received data-i18n attributes.

User-facing result: In Chinese mode, Tips #2 and #4 remained in English while #1 and #3 translated correctly. Subtle. Embarrassing.

Rule I now enforce: Every batch migration script must include a verification pass:

expected_keys = ['tip1', 'tip2', 'tip3', 'tip4', 'why1Title', ...]
for key in expected_keys:
    if f'data-i18n="{key}"' not in html:
        print(f'MISSING: {key}')

Bug 3: innerHTML vs. SVG Children

<!-- WRONG: applyI18n() calls innerHTML on this button, wiping the SVG -->
<button data-i18n="ariaMenu" aria-label="Menu">
  <svg>...</svg>
</button>

<!-- RIGHT: translate aria-label in swapLang(), keep SVG intact -->
<button id="hero-hamburger" aria-label="Menu">
  <svg>...</svg>
</button>

applyI18n() assigns innerHTML for all non-input elements. If you put data-i18n on a parent containing an SVG icon, the icon disappears and gets replaced by raw text. The fix: keep data-i18n on sibling <span> elements, never on structural parents.

Bug 4: The Nginx Header That Broke Every Camera

The Microphone & Camera Test was failing on every device with Permissions policy violation. Users thought they had blocked access. They hadn't.

The Nginx server was sending: Permissions-Policy: camera=(), microphone=()

This header silently forbade all camera and microphone access before the browser even asked the user. The fix was a single Nginx config line change: camera=(self), microphone=(self). Users now see the normal browser permission prompt.

I also updated the deny-state copy in mic-camera-test.js (both English and Chinese) to explain that multi-device failures usually indicate a server header issue, not user settings.


Part VI: The Human Debug — When the Compiler Can't Help

On June 10, the developer's journal in our changelog reads:

"I woke up feeling off. Not the usual 'stayed up too late coding' off — something worse. Ended up at the hospital in the morning instead of at my desk. Lost half a day that I couldn't afford to lose. My body's been deteriorating for a while now. Too many late nights, too much coffee, not enough sleep, not enough care."

I shipped three tools that day. Only two made it. One of them was only 60% done.

On June 11, the entry isn't about code at all:

"When you pour real feeling into something and get indifference in return, the math never balances. You keep re-checking the equation hoping the answer changes. It doesn't. The hardest part isn't the leaving; it's that you genuinely cared and they genuinely didn't. But the only way past that truth is through it."

"To anyone who's ever felt like leaving this world because of one person, one heartbreak, one season of pain: don't. The world is wider than the wound. Green hills await wherever you roam."

The road trip was supposed to be June 12–15. Xinxiang, Xinyang, maybe Hunan. Two days of driving, windows down, nowhere to be. It got postponed. The keyboard won.

On June 8, I asked the AI Tarot: "Is there still a chance with her?" The three-card spread returned Judgement (Past) → The Devil (Present) → The Lovers (Future).

The AI read Judgement as a necessary awakening that had already happened — not a random ending, but a reckoning. The Devil nailed the present: caught in the grip of attachment, tethered by old patterns. And The Lovers: yes, there is a possibility — but only through authentic alignment, not compulsion.

It was unsettlingly accurate. The cards saw what I already knew but wouldn't admit.

Building in public means documenting the human bugs too. The insomnia. The weight loss (70.5 kg → 68.5 kg in two weeks). The appetite that vanished. The Douyin follow-then-unfollow at midnight. These are the entries that don't make it to the GitHub release notes but matter more than version bumps.


Part VII: The Numbers & What Honesty Looks Like

Metric Value
Total tools shipped 86 (85 in /tools/ + 1 root feature page)
Tools killed after shipping 2 (AI Pixel Art GIF ×2 attempts, Live Photo Frame)
Bilingual pages migrated 19
Legacy pages remaining 67
Blog posts published 91 (84 guides + 9 Tool Tales)
Changelog entries 46
Service Worker version toolknit-v119
AI token spend (peak day) $200
Hospital visits during build 2
Lines of code written I stopped counting after the insomnia started

What's Next (The Arithmetic of Patience)

The remaining 67 legacy pages will migrate gradually. Each one requires:

  • HTML structural overhaul (hero top-bar with language toggle, FAQ accordion, responsive footer-grid)
  • data-i18n / data-i18n-common attribute injection across all static text
  • Page-specific i18n dictionary creation (30–60 keys per page)
  • JSON-LD schema updates (SoftwareApplication + FAQPage + BreadcrumbList)
  • Manual testing: JS syntax validation, language toggle verification, mobile responsive check
  • Service Worker cache version bump

I won't rush it. The architecture is solid. The lesson from Live Photo Frame is clear: ship what you're proud of, even if that means shipping less.

The next batch will likely be the remaining PDF tools and video converters. Each shared library (convert-image.js, compress-image.js) reduces the per-page migration cost — the shared t() calls are already defined; only the HTML markup needs updating.


The Ask

If you've ever built something, shipped it, then had to kill it after two days of debugging — you're not alone.

If you've ever stared at an AI token bill and wondered if the architecture is worth the arithmetic — same.

If you've ever shipped code from a hospital waiting room, or debugged a production bug at 3 AM while your body begged for sleep — I see you.

ToolKnit is free. No signup. No uploads. All processing is client-side. Read the full changelog — it's more honest than most build logs.

Built with love. Paid in tokens. Still shipping.


Originally published as a raw build log from a solo indie project. No affiliate links. No sponsor reads. Just code, costs, hospital visits, and the occasional 3 AM SyntaxError. If you're building something alone right now — keep going. The grey skies pass. They always do.