Skip to content
AFM Studio
  • Pages

    AFM Studio
  • Work
  • Lab
  • About
  • CV
  • Contact
  • Projects

    AksaraA searchable library for Otorita IKN's knowledge, where the server checks every reader's access across three levels
  • Daily TaqwaTamper-resistant prayer attendance system for ~2,000 OIKN government employees at Masjid Negara IKN
  • Lantara v2A permit engine for Otorita IKN where administrators add new permit types from a screen, without a developer
  • eKiosk IKNTwo control rooms for Nusantara: kiosk content management and live bus tracking with arrival times that grade themselves
  • E-Monev IKNA monitoring platform for Nusantara's 23-year build, tracking every unit's quarterly progress against 24 national KPIs
  • JDIH OIKNOIKN's public legal document portal, with a controlled publishing workflow and AI summaries that never leave the server
  • Jejak PenanamanA check-in app for IKN tree-planting events that proves each participant was on site with a geofenced selfie
  • Dashboard SAKTIA nightly pipeline and dashboard that replaces OIKN's manual budget downloads for all six of its budget units
  • DoseRxA bedside dose calculator for 127 drugs that rounds each dose to tablets and syrups Indonesian pharmacies sell
  • CubiqA speedcubing trainer with competition-grade timing and a solver I wrote for each of the 8 major puzzles
  • ScimotionAn interactive science library of 86 articles, each explained next to animations the reader can control
  • WA Daily Scrum BotA WhatsApp bot that chases missing standup reports for a 14-person team and flags tasks stuck for days

↑ ↓ to move · Enter to open · Esc to close

Sketch Lab

An interactive lab for four probabilistic data structures, with the exact answer running alongside to expose every error

Screenshots
structures implemented from the original papers
4
every memory figure measured, never estimated
Real bytes
runtime dependencies, no sketch libraries
3
On this page

The problem

Backend engineers have all heard of Bloom filters, Count-Min sketches and HyperLogLog. Few feel confident using one. These structures answer questions about huge data in a small, fixed amount of memory: a database skipping a disk read, a CDN counting unique visitors, a stream processor finding its most frequent items. The papers are dense, and most blog posts flatten the one thing that matters. The error is one-sided and can be calculated in advance. A Bloom filter's "no" is certain. Its "yes" is only likely.

Nobody adopts an approximate structure they can't check. I built Sketch Lab for two groups: engineers who need to size one for a production system, and engineers who need to see it work before they trust it.

What I built

The exact answer runs beside the approximate one

Every stream goes through the sketch and through an ordinary exact set or map at the same time. The page flags each false positive the moment it happens, naming the item and the slots it collided with. The main control is a memory slider. Drag memory down and two curves move together: the error the formula predicts, and the error measured from the stream.

Memory is measured in real bytes

The comparison with the exact structures only means something if the numbers are true. So every memory figure is the actual size of the allocated arrays, never a formula presented as a measurement. A calculator takes the number of items and a target error, and returns the sizing, the formula and the memory in real bytes. That screen is what makes it a tool someone can use at work.

I wrote the hash and the structures myself

All four structures come from their original papers: Bloom (1970), Count-Min (2005), HyperLogLog (2007) and a Cuckoo filter that supports deletion. I wrote the hash function too. Every error guarantee assumes a good hash, and a bad one breaks them silently. The hash tests run first in the build pipeline, and nothing else runs if they fail.

Guarantees and probabilities are tested apart

"A Bloom filter never says no to an item it holds" is a rule, and I test it with zero tolerance. "The error rate stays within the predicted bound" is a probability, tested with fixed seeds, many trials and a written-down tolerance. Keeping them apart stops anyone from adding slack to a rule that has none. Every run is seeded, so the same input gives the same result on any machine.

When a test failed, I fixed the model

One test failed at about 2,500 times the textbook error rate. I traced it to a structural effect of the hashing method when a filter has far more memory than it needs. I added the missing term to the model and left the tolerance alone. A later check showed this Cuckoo filter uses more real memory than a Bloom filter at every error rate. So the page offers it for deletion, and makes no claim about saving space.

Result

Sketch Lab is live, public, in English and Indonesian, and works offline after the first load. It covers four structures plus the exact reference, merge checks for combining sketches, and uniform, skewed, adversarial and text streams. Every setting can be shared as a link. Every milestone in the plan shipped, including the optional Cuckoo filter. I built it in one day, on 6 August 2026, with three runtime dependencies. It's a teaching instrument, not a production library, and that was the plan from the start.

Under the hoodTechnical detail for engineers
  • MurmurHash3 32-bit with Kirsch-Mitzenmacher k-hash derivation, written by hand. The hash suite covers avalanche, chi-squared and known vectors, and is the first of five sequenced CI jobs: hash gate → absolute → bounds/merge → build → deploy.
  • lib/pds and lib/hash are pure: typed arrays in, typed arrays out, no DOM, clock or module state. The same code runs in the browser and under Node in tests. Seeded PRNG is sfc32, with no unseeded Math.random in lib/.
  • tests/absolute/ allows no tolerance. tests/bounds/ asserts on counts with a Poisson tail, because a normal band around a sub-unit mean (λ = 0.05) fails on a single ordinary event.
  • The coincidence floor: at m=65536, k=8, n=500, Kirsch-Mitzenmacher's arithmetic-progression slots let one inserted item cover all k slots of a probe. The derived floor is (4n/m²)·(1+f)/(1−f) and now matches measurement (commit cb8c860).
  • Forcing h₂'s low bit to 1 is right for Bloom but left the Cuckoo filter only 2^(f−1) fingerprints, measured as exactly 2.0× its bound. Fixed by shifting past the forced bit (c08fb0a).
  • A Zipfian stream failed 906 of 2,000 Cuckoo inserts at 27% load; at most 2b copies of one item fit. Both this and deletion's false negatives are asserted to occur. Bloom-vs-Cuckoo crossover is ε ≈ 0.2%, not the often-cited 3%, which belongs to the semi-sorted variant.
  • Merges: Bloom ORs, HyperLogLog takes the per-register max, Count-Min adds. Each union is checked against the sketch of the concatenated stream. HyperLogLog includes the small-range linear-counting regime.
  • Long runs go to a Web Worker with transferred ArrayBuffers. Bit arrays render on Canvas, error curves in SVG; above a size threshold bits are drawn as block densities and the UI says so.
  • Brass shipped at 2.84:1 contrast; I darkened it to match the truth green's contrast exactly (5.16 / 5.55 / 4.62). upload-pages-artifact v4 dropped .nojekyll, so Actions are now bumped one major at a time.
  • Next.js 14 static export, 7 routes × 2 locales, 87.3 kB shared first-load JS. 328 assertions across 13 files. About 11,900 lines of TypeScript, 38 commits over roughly 12 hours.

Screenshots

Landing Page
Bloom Filter
Count-Min Sketch
HyperLogLog
Cuckoo Filter
Compare and Merge

Hiring, or building something similar?

I'm open to full-time roles and select freelance work.