A tool library with no server behind it
A growing directory of single-purpose web tools, pre-rendered to static HTML and served from an assets-only Cloudflare Worker — zero server execution, and every file stays on the visitor’s device.
- Role
- Creator & Developer
- Year
- 2026
- Hosting
- Fully pre-rendered static pages on an assets-only Cloudflare Worker — no entry script, so no isolate is ever spawned for a page request
- Live
- floi.dev





- Worker invocations per page load
- 0
- In-browser, nothing uploaded
- 100%
- Tools shelved across 6 categories
- 35
Overview
Free online tools are a category built on a bad trade: you hand a stranger’s server your video, your PDF, or your API token, and hope. floi is the opposite bet. Every tool is a pre-rendered page on a CDN and all the work happens in the visitor’s own browser, so the privacy claim is not a policy on a page — it is the architecture, and there is no backend that could receive a file even if it wanted to.
I built it solo as a directory rather than a single product: six shelves (media, text, developer, documents, study, design) with four media tools live and the rest visibly in build. The engineering interest is in what a static origin can actually do — Canvas, the Screen Capture API and YouTube’s IFrame Player API doing real media work — and in refusing every shortcut that would have quietly added a server.
What the build involved
- Built the site in Astro 7 with static output, no UI framework and no CSS framework — one stylesheet of semantic tokens, plain JS modules per tool, and a /design-system/ page that renders the real stylesheet as its own reference.
- Deployed to a deliberately assets-only Cloudflare Worker: wrangler.jsonc has no `main` entry point, so requests are served straight from static asset storage and no isolate ever runs. Verified on the live deployment at 100% head sampling.
- Shipped each tool as one page that is a document until the visitor commits, then becomes a full-viewport workspace via a class on <html> — so a crawler always sees the complete page and nothing needs JavaScript to be indexed.
- Drove the whole library from one data file: the hub grid, search index, nav, sitemap priorities, JSON-LD and social cards all derive from it, with counts computed rather than typed and planned tools structurally barred from structured data.
- Wrote a generated /llms.txt that states what the site can actually do today, plus a git-derived sitemap lastmod, so neither answer engines nor crawlers are given a date or a capability that is not true.
- Rebuilt the optional local companion server (Node built-ins only, zero npm dependencies) after finding a command-injection hole in the original: argument arrays instead of shell strings, loopback-only binding, and a strict 11-character video-ID check.
Challenges & solutions
Challenge
Pulling an arbitrary frame out of a YouTube URL is blocked at the browser’s origin boundary — the iframe is cross-origin, the video stream sends no CORS headers, and the player API returns 403.
Solution
Rather than fake it, I mapped what genuinely works from a static origin and shipped three honest modes: published thumbnails via the CORS-enabled image host, any-moment capture through the Screen Capture API, and frame-accurate extraction from a local file. Each page states its own ceiling instead of implying one it cannot reach.
Challenge
Screen capture records rendered pixels, so a paused YouTube player bakes its own controls and title gradient into every shot — the exact thing the tool exists to avoid.
Solution
Freeze pauses our preview element rather than their player: the displayed frame holds still while the stream keeps running, so the export is drawn from a clean frame. Region Capture crops the stream to the player element so no browser chrome survives into the image.
Challenge
Stepping frames accurately means knowing the real frame rate, and neither a video element nor YouTube’s API will simply tell you.
Solution
The rate is measured from the decoder via requestVideoFrameCallback intervals, with a frame-count fallback that keeps working in background tabs, snapped to standard rates and overridable in the UI. Assuming 1/30s silently drifts on 24, 25, 50 and 60 fps footage.
Technologies
Available for new work
Want results like these?
Tell me what you are building. I will reply with a clear scope and a fixed quote.