The groove is the 40 milliseconds a grid throws away, so we built a beat studio that gives them back, in a browser tab
The groove is the 40 milliseconds a grid throws away, so we built a beat studio that gives them back, in a browser tab
The Problem: Timing Precision Lost by Standard Grids
At 90 BPM, a sixteenth note spans 166.7 ms. A typical step grid snaps every hit onto these 166.7 ms boundaries and discards 40 ms entirely. That gap is the difference between a hi-hat that marches in perfect time and one that bounces-something shorter than a single frame of most phone videos. The authors built a beat studio that keeps those microseconds instead of throwing them away.
The Solution: Krisp - A Browser-Based Beat Studio
Krisp is a minimal, hardware-feel beat studio running entirely in a browser tab. It lets you:
- Tap beats on pads and shape them with velocity and swing
- Use open-source models to humanize or vary your beats
- Describe a sound and receive a unique output from ElevenLabs
- Arrange beats into songs, mix them through a real master chain, and export WAV or MIDI for your DAW
For a friend named Anuv who makes beats, Krisp means his ideas stay alive outside voice memos and scattered files. Every idea is one click from home, and everything is visible without hiding behind menus or modes.
Architecture & Tech Stack
The application is built on a combination of open-source libraries and custom infrastructure:
- Frontend: Zustand for state management, Tone.js for audio scheduling, Magenta.js for AI sound generation
- Database: Tiger Data (PostgreSQL with
pgvectorfor 384-dimensional embeddings and full-text search) - AI Services: ElevenLabs for generating one-shot or looped sounds, Magenta.js models (GrooVAE, MusicVAE, DrumRNN) for humanization and variation
- Deployment: Vercel (iads for functions, main app at
/api) - Agent Integration: Entire (Claude Code) captures agent sessions and commits
Core Components
| Component | Technology | Purpose |
|---|---|---|
| State Management |
zustand
|
One-workspace object with undo history |
| Scheduling | Pure functions (stepSeconds, swingDelay, hitTime) |
Per-tick timing logic |
| Audio Engine | Tone.js → Tone.Offline |
Real-time rendering with 100 ms look-ahead |
| AI Models | Magenta.js (GrooVAE, MusicVAE, DrumRNN) | Humanize, vary, continue, morph beats |
| Sound Generation | ElevenLabs API | Generate custom sounds from prompts |
Features
Workspaces
- Start empty or choose from eight original patterns (each with its own tempo and swing)
- Eight preset beats provide immediate inspiration
- Automatic saving with visual feedback (amber → green LED)
Beat Editing
- Grid-based placement with drag-and-paint rows
- Shift-click for ghost notes or accents
- Hit
RECto play a row directly from the keyboard - Velocity and swing are preserved when painting rows
Sounds & AI
- Presets and Create tabs for quick access to sounds
- Create uses ElevenLabs to generate one-shot or seamless loops
- Upload & Library organizes sounds into playlists and allows searching by name or vibe
- AI Tab offers Humanize, Variations, Continue, and Morph (Stage 4)
Mixing
- Every sound gets EQ, reverb send, pan, and fader
- Reverb return and master chain with compressor, gain reduction, and limiter
- A sketch leaves Krisp already mixed, producing a finished-sounding WAV
Implementation Details
The Scheduler
All timing is handled by three pure functions that run without a sound card:
export const stepSeconds = (bpm: number) => 60 / bpm / 4;
export function swingDelay(stepIndex: number, swing: number, stepSec: number) {
return stepIndex % 2 === 1 ? (swing * stepSec) / 2 : 0;
}
export function hitTime(tickTime: number, stepIndex: number,
step: { offset: number }, swing: number, stepSec: number) {
const t = tickTime + swingDelay(stepIndex, swing, stepSec) +
step.offset * stepSec;
return Math.max(t, tickTime - 0.4 * stepSec);
}
The Transport never loops; the loop region is applied via wrapTick, ensuring drum hits and audio clips stay synchronized.
Exported Audio
Rather than using a separate offline renderer (which would introduce divergence), Krisp renders directly in the browser:
const rendered = await Tone.Offline(
async (ctx) => {
const engine = createEngine(ctx);
engine.sync(ws);
await engine.ready;
// Reverb impulse response binding
ctx.transport.start(0);
},
seconds + 2, 2, 44100
);
A 16-bar song at 90 BPM takes 42.7 seconds of music. With a 2-second reverb tail, it renders in 16.1 seconds on a GPU-less laptop-about 2.7× faster than real time-while following the arrangement exactly.
AI Behavior
The AI never composes beats from scratch. Instead, it modifies existing beats:
- Humanize adjusts velocities and timing of existing steps (cannot add or remove notes)
- Variations generates nearby candidates based on groove similarity and temperature
- Continue writes bar 2 from bar 1
- Morph transforms one beat into another across nine steps
Two important design decisions emerged during testing:
-
MusicVAE and DrumRNN return quantized notes with velocity unset, defaulting to 100. GrooVAE returns unquantized notes (start time, not a step); Krisp recovers the step as
round(startTime / stepSeconds)and treats the remainder as an offset clamped to ±½ step-the "groove." -
Snare and Clap share drum pitch 38. To prevent model-generated snares from wiping out clap rows, model notes are routed to the first available slot with matching pitch, while other notes retain their own steps.
ElevenLabs Integration
ElevenLabs serves as the sound generator, creating one-shot or seamless loops from textual descriptions. The endpoint supports parameters like loop: true and duration_seconds. The API call example:
POST https://api.elevenlabs.io/v1/sound-generation
?output_format=mp3_44100_128
&text="warm dusty kick"
&duration_seconds=2
&prompt_influence=0.5
&loop=true
Every generated sound is shaped, shown, and stored transparently-no hidden magic boxes.
Building Process
Krisp was developed within the Hacktoberfest Weekend Challenge (Build for a Friend, October 2026). The codebase lives at krisp/krisp.
Tech Stack Summary
| Layer | Technology |
|---|---|
| Frontend State |
zustand
|
| Audio Engine | Tone.js + Tone.Offline |
| AI Models | Magenta.js (GrooVAE, MusicVAE, DrumRNN) |
| Database | Tiger Data (PostgreSQL + pgvector + full-text) |
| AI Service | ElevenLabs (server-side key generation) |
| Deployment | Vercel (functions on iad1, main app at /api) |
| Agent Capture | Entire (Claude Code) |
Setup
git clone https://github.com/Dronzer2code/Krisp
cd Krisp
cp .env.example .env
npm install
# Paste db/schema.sql into the Tiger Data SQL editor once
npx vercel dev
npm run test # 146 unit tests
npm run build && bash scripts/check-secrets.sh # no key may appear in dist/
Usage
- Visit https://krisp.vercel.app
- Enter the passcode
subaru2007 - Navigate to New workspace → Lo-fi → Create
- Tap pads in the snare row to cycle ghost → soft → normal → accent
- Adjust Swing and use the AI tab → Humanize to refine beats
- Export your final track as WAV or MIDI
Verification
The author confirms the timing accuracy by comparing a manually created hi-hat pattern against Krisp's export. At 90 BPM with 50% swing, every second hat should land approximately 41 ms late, and even hats should move by less than 0.02 of a step. The actual results show odd hats landing 0.24 of a step late (40 ms vs. the formula's 41.7 ms) and even hats moving by under 0.02 of a step-proving the exported WAV uses the same engine as the source.
Conclusion
Krisp demonstrates that preserving microsecond-level timing isn't just about better audio-it's about giving producers the precise control they need to express their groove. By keeping the 40 milliseconds that traditional step grids discard, it enables a new kind of interactive beat-making experience that feels both hardware-native and accessible in a web browser.
Comments
No comments yet. Start the discussion.