Building tapdot tools: 90+ single-purpose utilities, zero backends, zero accounts
The origin story
I got tired of counting words for a blog draft on a site that wanted to email me a "productivity report" afterward. Then I needed to format some JSON and landed on a tool wrapped in ad networks with a cookie banner that took longer to dismiss than the formatting took to run.
That's the origin story: every small utility I needed lived on a site that wanted something back - an email, a tracking pixel, an account "to save my history." So I built tapdot tools - 92 tools now, one job each, no backend, no build step, no accounts, nothing you type ever sent anywhere unless the page says otherwise in plain English. This is the build log.
The pain point, specifically
A JSON formatter is maybe 40 lines of JS - building one isn't hard. The problem is the business model around free web utilities: it's almost always ads or data, because a one-off tool has no other way to pay for itself. You end up on a fragmented web where every tool lives on a different domain with a different privacy policy, half of which you didn't read.
The fix isn't a better tool. It's removing the reason to ever ask the question. If nothing leaves the browser, there's no policy to worry about.
Static, browser-only, no exceptions without a receipt
Every tool is plain HTML/CSS/JS in its own folder - no React, no bundler, no build step. That's not a performance flex, it's a trust mechanism: you can view-source any tool and see exactly what runs, because what runs is the source.
The rule I hold hardest: any tool that talks to the network has to disclose it, specifically, in the privacy policy. Right now that's two exceptions out of 92 tools - an optional URL-lookup proxy for one citation tool, and a currency exchange-rate fetch (rates only, never your amounts). Everything else is closed off from the network entirely by construction, not by promise.
I did prototype a dictation tool using the browser's speech recognition API, but killed it before shipping - more on why below.
Making 92 static pages feel like one app, without a router
The hard part of "no backend, no framework" is that plain multi-page sites feel like the '90s - full reload, white flash, lost scroll position. Two browser features did the whole job:
@view-transition { navigation: auto; }
That animates the swap between pages natively - no client-side router, no virtual DOM. Pair it with Speculation Rules so hovering a link prerenders the destination in the background before you even click:
const specScript = document.createElement('script');
specScript.type = 'speculationrules';
specScript.textContent = JSON.stringify({
prerender: [{
where: { href_matches: '/*' },
eagerness: 'moderate'
}]
});
document.head.appendChild(specScript);
By the time your cursor lands on a link, the next page is often already rendered. It feels instant - two browser primitives, not a framework.
The CSS bug that kept coming back
This one bit me four separate times before it clicked. Cards would overflow on mobile even though every child inside was already told to shrink. Turns out CSS Grid and Flexbox children have an implicit min-size: auto - they won't shrink below their content's intrinsic width unless you override it, and that has to happen at every level of nesting, not just the outer container:
.ts-select { min-width: 0; max-width: 100%; }
.ts-workbench > * { min-width: 0; }
.dev-row { min-width: 0; }
Fix one nested grid, ship a new tool with a nested grid three weeks later, hit the exact same overflow again. It's now a standing rule in the project: any new nested grid/flex layout gets min-width: 0 at every level, on sight, because I got tired of rediscovering the same CSS spec behavior.
Real crypto, not remembered crypto
When I added a password-hashing tool, the tempting shortcut was writing bcrypt from memory. I didn't. Hand-transcribing Blowfish S-boxes or an Argon2 mixing function from memory, in a tool whose whole job is producing correct hashes, is exactly where "roughly remember" becomes a silent bug nobody catches until it matters.
Instead I vendored hash-wasm's actual WASM builds of the reference C implementations - self-contained, embedded as base64, no runtime fetch - and wrote round-trip tests (hash → verify → reject-wrong-password) before trusting it. Local-only and "don't write your own crypto" pulled in the same direction here.
Gating on-device AI without lying about it
A few tools use Chrome's built-in on-device model APIs - genuinely on-device, no server round-trip. But "on-device AI" fails in more states than "works" or "doesn't": model not downloaded, unsupported GPU, wrong browser. I wanted to avoid a dead button with a spinner.
async function checkSupport() {
if (!('Writer' in self)) {
gate('no', 'Not available in this browser');
return;
}
const avail = await Writer.availability();
if (avail === 'unavailable') {
gate('no', "This device can't run the model");
return;
}
gate('ok', avail === 'available' ? 'Ready' : 'Will download on first use');
}
The rule that came out of this: only call availability() passively on load. Never spin up a real session just to check if you can - that's what actually triggers the download, so it has to happen on a genuine click, not a background probe.
The tool I killed on principle
I built a dictation tool using the browser's built-in speech recognition API and got it fully working - live transcript, punctuation voice commands, the works. Then I pulled it before shipping.
Chrome's SpeechRecognition sends your microphone audio to Google's speech-to-text servers to produce the transcript - there's no way around that with today's Web Speech API. I tried making the exception loud instead of quiet: a callout above the tool, a dedicated privacy-policy section, footer text spelling out exactly what happens. It still didn't sit right.
If "nothing leaves your device" has an asterisk, it's not really the promise anymore, disclosure or not - so the tool didn't ship. Better to have 92 tools that all mean the same thing than 93 where one needs a footnote.
The full list - all 92 tools, by collection
Everything below runs at tools.tapdot.org - click any name to open it directly.
📚 Study (4)
- CiteMaker - APA, MLA, Chicago, Harvard citations
- FlashForge - Flashcards with spaced repetition
- GradeCalc - GPA, weighted grade, final exam calculator
- BiasCheck - Media bias & loaded language analyser
✍️ Write (4)
- ReadScore - Flesch-Kincaid readability analysis
- WordCount Pro - Word count, keyword density, live
- LoremCraft - Placeholder text in 10 styles
- ThreadCraft - Split long text into a tweet thread
🛠️ Dev (27)
- JSONLab - Format, validate, minify, diff JSON
- JSONConvert - JSON to YAML, CSV, XML, TOML
- JWTStudio - Decode & inspect JWT tokens
- YAMLCheck - YAML validator & formatter
- CSVExplore - Sortable, filterable CSV viewer
- MarkdownLive - Markdown editor with live preview
- HTMLPreview - Sandboxed HTML renderer
- SQLFormat - SQL formatter & beautifier
- ColourContrast - WCAG AA/AAA contrast checker
- UUIDGen - UUID v4/v7, ULID, nanoid generator
- TimezoneNow - World clock & meeting overlap finder
- TZConvert - Convert a date & time across timezones
- RegexLab - Regex tester with match highlighting
- CronLab - Cron expression workbench
- Base64Tool - Encode and decode Base64 live - standard or URL-safe
- DiffCheck - Line-by-line text comparison, fully local
- WSTester - Connect and test WebSocket servers from your browser
- CodePlay - HTML/CSS/JS playground with live preview and console
- BigOCheck - Estimate time complexity from code structure
- WorldClock - Airport-style analog world clocks, TV fullscreen
- TimestampConvert - Unix timestamp <-> date, live, across timezones
- JSONCSV - Convert JSON arrays to CSV and back
- HashGen - SHA-1/256/384/512 hashes for text or files
- KeyGen - RSA/ECDSA keypair generator, exported as PEM
- PassHash - Hash and verify passwords with real bcrypt/Argon2id (WASM)
- SVGClean - Strip editor cruft, comments, and excess precision from SVG markup
- SQLObfuscate - Anonymize table/column names and literals in a SQL query
📣 Marketing (8)
- UTMBuilder - Build UTM tracking links - history saved locally
- HeadlineScore - Score headlines for clarity, emotion, and SEO
- EmailSubjectTester - Spam triggers, mobile preview, sentiment
- AdCopyWriter - Google, Meta and LinkedIn ad copy from a brief
- SocialCalendar - Plan your content calendar - saved locally
- PersonaBuilder - AI-assisted customer personas, saved locally
- CompetitorMatrix - Feature comparison matrix - export CSV or Markdown
- ROICalculator - ROI, ROAS, CPA, CLV and payback period
💰 Finance (11)
- CompoundCalc - Compound interest with monthly contributions
- BudgetPlanner - 50/30/20 budget split with a donut chart
- MortgageCalc - EMI, amortisation schedule, overpayment savings
- InvestmentTracker - Portfolio holdings, returns, and allocation
- TaxEstimate - Income tax for India, US or UK
- CurrencyConvert - 170+ currencies - rates cached daily
- EquityCalc - Cap table dilution across funding rounds
- NetWorthTracker - Assets minus liabilities, tracked monthly
- LoanCalc - EMI, amortisation, and part-payment impact for any loan
- RetireCalc - Retirement corpus needed vs on track - and the gap
- InflationCalc - What inflation does to your money over time
⚖️ Legal (6)
- ContractRead - Plain-English contract summary - on-device AI
- NDAGenerator - Mutual or one-way NDA from a template
- PrivacyPolicyGen - GDPR/CCPA-aware privacy policy generator
- TermsBuilder - Terms of Service from a template
- CopyrightChecker - Estimate public-domain status by jurisdiction
- LegalGlossary - 130+ legal terms explained in plain English
🧑💼 HR (6)
- SalaryBand - Pay grades, compa-ratio, range visualisation
- JobDescriptionWriter - Inclusive job descriptions via on-device AI
- InterviewKit - Role-specific questions and a scoring rubric
- OfferLetterBuilder - Professional offer letter from a template
- OnboardingChecklist - Day 1 / Week 1 / Month 1 checklist, saved locally
- LeaveCalculator - Accrual, balance, and carry-forward
🩺 Health (6)
- BMICalc - BMI, BMR, TDEE and healthy weight range
- MedicationLog - Medications, doses and adherence rate
- SymptomDiary - Log symptoms and severity on a calendar
- CycleTracker - Predict next period and fertile window
- WaterIntake - One-tap hydration logging and streaks
- SleepLog - Sleep duration, quality and AI pattern analysis
🎨 Design (9)
- SketchPad - Local whiteboard - pen, shapes, arrows, text, PNG export
- PhotoTune - Local photo editor - filters, rotate, resize, export
- PaletteForge - Colour palettes with CSS/Tailwind export and contrast ratios
- TypographyScale - A harmonious 9-step type scale from base size and ratio
- IconExplorer - A curated outline icon set - copy as SVG, JSX, or Vue
- ShadowStudio - Layered CSS box shadows visually, with presets
- SpacingCalc - Linear or multiplicative spacing scale, with export
- GradientMaker - Linear, radial, and conic CSS gradients visually
- ImageCompress - Resize and recompress images locally - JPEG/WebP/PNG
✅ Productivity (7)
- UnitConvert - Every common unit converted live, with full tables
- FocusTimer - Pomodoro-style focus sessions with notifications
- QuickNote - A fast, private notepad that autosaves as you type
- DecisionMatrix - Weigh criteria, rate options, see which wins
- MeetingTimer - See the real cost of your meeting, live
- HabitTracker - Daily habits with streaks and a heatmap
- ReadingList - Save articles and books, track status and notes
🤖 Chrome AI (4)
- AISummarize - Summarize long text on-device - TL;DR, key points, headline
- AITranslate - Translate between languages on-device with auto-detection
- AIWrite - Draft an email, post, or message from bullet points - on-device
- AIRewrite - Change the tone or length of text - on-device
What's next
The repo's public: github.com/mohan-venkatakrishnan/tapdot-tools. I'd genuinely like feedback on where I drew the line with the dictation tool - was killing it the right call, or is a loud, honest disclosure enough to justify keeping a tool that isn't fully local? Curious how others building free-but-honest tools have drawn that line.
And if there's some small utility you keep reaching for that always comes bundled with ads, logins, or a privacy policy you'd rather not read - tell me in the comments. If it can be done fully client-side, I'll build it.
Comments
No comments yet. Start the discussion.