The groove is the 40 milliseconds a grid throws away, so we built a beat studio that gives them back, in a browser tab
DEV Community

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 pgvector for 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 REC to 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:

  1. 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."

  2. 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

  1. Visit https://krisp.vercel.app
  2. Enter the passcode subaru2007
  3. Navigate to New workspace → Lo-fi → Create
  4. Tap pads in the snare row to cycle ghost → soft → normal → accent
  5. Adjust Swing and use the AI tab → Humanize to refine beats
  6. 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.

Read on DEV Community ↗ ← Back to News

Comments

No comments yet. Start the discussion.