mobile · July 2026
TTAB
A fast, offline metronome with tap tempo, built on a sample-accurate audio clock. One Expo codebase runs native in Expo Go and on the web — playable in the phone frame on this page. Measured timing: 0.0000 ms interval deviation, sample-exact.
Tornike Kalandadze — software engineer & technical lead, client delivery since 2019 · Tbilisi (GMT+4)

Problem
A metronome built on JS timers is unusable for musicians: setInterval wakes late (±5–30 ms, worse under load) and every error lands directly in the click as drift and flams. The extra constraint: it had to run in stock Expo Go — and Expo Go ships no audio API that can schedule playback ahead of time.
Approach
Use the lookahead pattern from "A Tale of Two Clocks": a coarse 25 ms scheduler pre-schedules clicks up to 180 ms ahead on AudioContext.currentTime, the audio hardware's sample clock, so JS lateness only eats lookahead margin — the sound is already queued. The real Web Audio API isn't in Expo Go's JS runtime, but react-native-webview is: the engine is ~150 lines of dependency-free JS in a hidden 1×1 px WebView, with a ScriptProcessorNode co-driving the scheduler from inside the audio pipeline so it survives WebView timer throttling. Visuals and haptics re-anchor to the audio clock on every beat.
Impact
Measured, not claimed (npm run timing-test drives the real engine in headless Chrome): 100 clicks rendered offline show 0.0000 ms mean and max interval deviation — sample-exact. The live scheduler at 300 BPM eighths with the JS thread frozen 150 ms every 500 ms: 0 late, 0 skipped over 300 clicks. On a real iPhone via Expo Go: 0 late, 0 skipped over 800+ clicks, 8.5 ms reported output latency.
Through every lens
02 · Mobile
The only project on this site you can operate without leaving it. One Expo codebase, exported to the web as static files, running at its real phone viewport inside the frame — not a screenshot of an app, the app.
Built with
Expo · React Native · React Native Web · TypeScript · Skia · Web Audio API · Reanimated
Notes
TTAB is a metronome where timing accuracy is the product. A metronome built on JS timers drifts and flams — setInterval wakes late by 5–30 ms and every error lands in the click. TTAB never plays a click from a JS timer: a coarse scheduler wakes every 25 ms and pre-schedules every click up to 180 ms ahead on the audio hardware's own sample clock, so the grid cannot drift by construction. The constraint was running in Expo Go with no dev build — expo-audio can't schedule playback, so the ~150-line dependency-free engine lives in a hidden 1×1 px WebView speaking a 4-message protocol to React Native, using the real Web Audio API that WKWebView and Android WebView expose. Clicks are synthesized sine bursts (no assets to decode), the Skia-rendered dial drags like a physical knob, tap tempo uses median outlier rejection, and the pendulum and haptics re-anchor to the audio clock on every beat so they can't accumulate desync. Offline-first: zero network, zero accounts, zero permissions.