Drum machine · grok-build-0.1
Build a 16-step drum machine with a clickable grid for kick, snare, hi-hat, and clap, all sounds synthesized with the Web Audio API, plus play/stop and an adjustable BPM.
transcript · 10 messages
Build a 16-step drum machine with a clickable grid for kick, snare, hi-hat, and clap, all sounds synthesized with the Web Audio API, plus play/stop and an adjustable BPM.
thinking
The task is: Build a 16-step drum machine with a clickable grid for kick, snare, hi-hat, and clap, all sounds synthesized with the Web Audio API, plus play/stop and an adjustable BPM.
{"command":"ls -la /tmp/ws"}total 0 drwxr-sr-x. 2 node node 6 Aug 1 19:55 . drwxrwsrwx. 3 root node 16 Aug 1 19:55 ..
thinking
The task is to build a single-page web app into one self-contained file named index.html in the current directory. It must be a 16-step drum machine with:
The task is to build a 16-step drum machine in a single HTML file using Web Audio API for sounds, clickable grid, play/stop, adjustable BPM. Everything in one file, no externals.
I need to create index.html in /tmp/ws.
I have a write tool to write the file.
First, I need to write the code for index.html.
Let me sketch the code.
<!DOCTYPE html>
<html>
<head>
<title>Drum Machine</title>
<style>
/* CSS here */
body { background: #222; color: white; font-family: Arial; }
#container { max-width: 800 }
#bpm { width: 100px; }
.grid { display: grid; grid-template-columns: 100px repeat(16, 50px); grid-template-rows: repeat(4, 50px); }
.instrument { background: #333; }
.kick { background: #f00; }
.snare { background: #00f; }
.hihat { background: #ff0; }
.clap { background: #0f0; }
.active { background: #fff; }
.playing { background: #ff0; }
button { padding: 10px; }
</style>
</head wait, fix.
Better define rows properly.
Use table or divs.
Let's use divs with grid.
For instruments: Kick, Snare, HiHat, Clap.
Labels on left.
Step numbers on top.
Cells clickable.
For sounds: define functions.
function playKick(time) {
let osc = audio ctx.createOscillator();
osc.type = 'sine';
osc.frequency.value = 80;
let gain = audioCtx.createGain();
gain.gain.setValueAtTime(0, time);
gain.gain.linearRampToValueAtTime(1, time + 0.01);
gain.gain.exponentialRampToValueAtTime(0.001, time + 0.2);
osc.connect(gain);
gain.connect(audioCtx.destination);
osc.start(time);
osc.stop(time + 0.2);
}
Simple kick.
For snare:
function playSnare(time) {
let noise = audioCtx.createBufferSource();
noise.buffer = noiseBuffer;
let filter = audioCtx.createBiquadFilter();
filter.type = 'bandpass';
filter.frequency.value = 1200;
filter.Q.value = 0.5;
let gain = audioCtx.createGain();
gain.gain.value = 0.5;
gain.gain.linearRampToValueAtTime(0.001, time + 0.2);
noise.connect(filter);
filter.connect(gain);
gain.connect(audioCtx.destination);
noise.start(time);
// add tone
let osc = audioCtx.createOscillator();
osc.type = 'square';
osc.frequency.value = 150;
let oscGain = audioCtx.createGain();
oscGain.gain.value = 0.3;
oscGain.gain.linearRampToValueAtTime(0.001, time + 0. 1);
osc.connect(oscGain);
oscGain.connect(audioCtx.destination);
osc.start(time);
osc.stop(time + 0.1);
}
Yes.
For hi-hat:
function playHiHat(time) {
let noise = audioCtx.createBufferSource();
noise.buffer = noiseBuffer;
let filter = audioCtx.createBiquadFilter();
filter.type = 'highpass';
filter.frequency.value = 7000;
let gain = audioCtx.createGain();
gain.gain.value = 0.5;
gain.gain.linearRampToValueAtTime(0.001, time + 0.05);
noise.connect(filter);
filter.connect(gain);
gain.connect(audioCtx.destination);
noise.start(time);
noise.stop(time + 0.1);
}
For clap:
function playClap(time) {
let noise = audioCtx.createBufferSource();
noise.buffer = noiseBuffer;
let filter = audioCtx.createBiquadFilter();
filter.type = 'bandpass';
filter.frequency.value = 1500;
filter.Q.value = 1;
let gain = audioCtx.createGain();
gain.gain.value = 0.4;
gain.gain linearRampToValueAtTime(0.001, time + 0.1);
noise.connect(filter);
filter.connect(gain);
gain.connect(audioCtx.destination);
noise.start(time);
noise.stop(time + 0.1);
// add second for body
// or use loop for 2-3
// for simplicity one, but to make better, loop.
}
To make clap better, use loop.
function playClap(time) {
for (let i = 0; i < 3; i++) {
let t = time + i * 0.02;
// same as above with gain adjusted
}
}
Yes.
For noiseBuffer:
function createNoiseBuffer() {
const length = audioCtx.sampleRate * 2;
const buffer = audioCtx.createBuffer(1, length, audioCtx.sampleRate);
const data = buffer.getChannelData(0);
for (let i = 0; i < length; i++) {
data[i] = Math.random() * 2 - 1;
}
return buffer;
}
Yes.
For grid: use JS to create the grid divs.
In script:
window.onload = function() {
audioCtx = new AudioContext();
noiseBuffer = createNoiseBuffer();
// create pattern
// create grid
const sequencer = document.getElementById('sequencer');
// add header
const header = document.createElement('div');
header.style.display = 'grid';
header.style.gridTemplateColumns = '100px repeat(16, 50px)';
header.style.gridTemplateRows = '1fr';
header.style.gap = '2px';
// label for header
const empty = document.createElement('div');
empty.textContent = 'Step';
header.appendChild(empty);
for(let i=0; i<16; i++) {
const h = document.createElement('div');
h.textContent = i+1;
h.style.background = '#333';
header.appendChild(h);
}
sequencer.appendChild(header);
// rows
const rows = [];
for(let r=0; r<4; r++) {
const row = document.createElement('div');
row.style.display = 'grid';
row.style.gridTemplateColumns = '100px repeat(16, 50px)';
row.style.gap = '2px';
const label = document.createElement('div');
label.textContent = ['Kick', 'Snare', 'HiHat', 'Clap'][r];
label.style.background = '#444';
row./tmp/ws/index.html (17906 bytes)Successfully wrote 17906 bytes to /tmp/ws/index.html
thinking
The user asked to build a 16-step drum machine as a single self-contained index.html file. I've done that. Now I need to verify it's present and works as expected.
{"command":"ls -la /tmp/ws/index.html && head -c 200 /tmp/ws/index.html && echo \"...\" && tail -c 100 /tmp/ws/index.html"}-rw-r--r--. 1 node node 17918 Aug 1 19:56 /tmp/ws/index.html <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>16-Step Drum Machine</title> <style> :root { --bg: #0... dEventListener('DOMContentLoaded', init); } else { init(); } })(); </script> </body> </html>
thinking
The file has been created successfully. It's a single self-contained index.html with everything inline.
{"command":"wc -l /tmp/ws/index.html && echo \"---\" && grep -c \"playKick\\|playSnare\\|playHiHat\\|playClap\" /tmp/ws/index.html && echo \698 /tmp/ws/index.html --- 12 sounds: 4 21
thinking
The file is complete and self-contained. It has:
Done. Created `/tmp/ws/index.html` — a fully self-contained 16-step drum machine with: - 4-track clickable grid (kick, snare, hi-hat, clap) - Pure Web Audio API synthesis (no samples or external files) - Play / Stop controls - Live BPM slider (40–240) - Pre-loaded demo pattern - Keyboard support (spacebar toggles playback) - Step preview by clicking column headers - Clear button - Zero external resources — runs completely offline Open `index.html` in any modern browser.
TAKE IT TO CODE
A single self-contained HTML file — copy it and open in any browser, or reuse the prompt with your own pi-agent run.
RUN
EVALUATION
16-step drum machine with kick/snare/hi-hat/clap grid, BPM slider, and playback step indicator advancing across the grid.
Vision grade · anthropic/claude-sonnet-5
MEASUREMENTS
Raw readings from the capture, not a verdict — the grade above is the only judgement. Percentages are how much of the screen changed; the measure under-reports sparse motion on a dark background.