oneshotlm
Compare all models on this prompt →

Piano synth · gemini-3-flash-preview

Using the pre-provided tone.min.js (global Tone), build a playable piano keyboard controlled by mouse and computer keys that synthesizes notes with an ADSR envelope and visual key feedback.

⛨ sandboxed Open in new tab ↗
transcript · 6 messages
user

Using the pre-provided tone.min.js (global Tone), build a playable piano keyboard controlled by mouse and computer keys that synthesizes notes with an ADSR envelope and visual key feedback.

assistant
thinking
**Building Piano Functionality**

I'm focusing on integrating `tone.min.js` into a single `index.html` file. The core task now is to implement the playable piano keyboard logic using the library's capabilities.

bash {"command":"ls tone.min.js"}
toolResult

tone.min.js

assistant
thinking
**Building the Core**

I'm now focusing on constructing the `index.html` to lay out the piano's structure. My plan involves using flexbox for white keys, absolute positioning for black keys, and then styling it all. The JavaScript side is set up with `Tone.PolySynth` and `Tone.Synth` ready for configuration.

write index.html (7763 bytes)
toolResult

Successfully wrote 7763 bytes to index.html

assistant
thinking
**Refining App Functionality**

I'm currently verifying that the keyboard input maps correctly and that I'm seeing appropriate visual cues for key presses. This step is crucial for ensuring a seamless user experience.

**Adjusting Keyboard Visuals**

I'm fine-tuning the CSS for the black keys. I'm exploring how to best achieve that visually distinct "in-between" placement, potentially with negative margins, to better mimic a real piano's appearance within the flexbox layout. The `.active` class toggle for feedback is working well.

The piano keyboard web app has been created in a single self-contained `index.html` file. It uses the local `tone.min.js` library to synthesize sounds using a `PolySynth` with a triangle oscillator and an ADSR envelope. The interface features: - **Visual Piano Keys**: Interactive white and black keys with responsive feedback. - **Mouse/Touch Support**: Click or tap keys to play notes. - **Keyboard Support**: Map piano notes to computer keys (A through K). - **ADSR Envelope**: Configured with attack (0.1s), decay (0.2s), sustain (0.5), and release (1.0s). - **Polyphony**: Play multiple notes simultaneously. To use the app, open `index.html` in a web browser and click the **"Click to Start Synth"** button to enable audio. ```html <!-- index.html --> <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Tone.js Piano</title> <script src="tone.min.js"></script> <style> body { font-family: sans-serif; display: flex; flex-direction: column; align-items: center; justify-content: center; height: 100vh; margin: 0; background-color: #2c3e50; color: white; user-select: none; } h1 { margin-bottom: 20px; } #piano { display: flex; position: relative; background: #333; padding: 10px; border-radius: 10px; box-shadow: 0 10px 30px rgba(0,0,0,0.5); } .key { width: 60px; height: 200px; border: 1px solid #000; box-sizing: border-box; cursor: pointer; display: flex; align-items: flex-end; justify-content: center; padding-bottom: 10px; transition: background-color 0.1s; } .white { background-color: white; color: #333; border-radius: 0 0 5px 5px; z-index: 1; } .white.active { background-color: #ddd; box-shadow: inset 0 5px 10px rgba(0,0,0,0.2); } .black { background-color: #111; color: white; width: 40px; height: 120px; margin-left: -20px; margin-right: -20px; z-index: 2; border-radius: 0 0 3px 3px; } .black.active { background-color: #444; box-shadow: inset 0 5px 10px rgba(0,0,0,0.5); } .label { pointer-events: none; font-size: 12px; font-weight: bold; text-align: center; } #start-overlay { position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: rgba(0,0,0,0.85); display: flex; align-items: center; justify-content: center; z-index: 100; } #start-button { padding: 20px 40px; font-size: 24px; cursor: pointer; background: #27ae60; color: white; border: none; border-radius: 5px; } #controls { margin-top: 30px; text-align: center; color: #bdc3c7; } kbd { background-color: #eee; border-radius: 3px; border: 1px solid #b4b4b4; color: #333; display: inline-block; font-size: .85em; padding: 2px 4px; } </style> </head> <body> <div id="start-overlay"> <button id="start-button">Click to Start Piano</button> </div> <h1>Tone.js Piano</h1> <div id="piano"> <div class="key white" data-note="C4" data-key="a"><span class="label">C<br>(A)</span></div> <div class="key black" data-note="C#4" data-key="w"><span class="label">C#<br>(W)</span></div> <div class="key white" data-note="D4" data-key="s"><span class="label">D<br>(S)</span></div> <div class="key black" data-note="D#4" data-key="e"><span class="label">D#<br>(E)</span></div> <div class="key white" data-note="E4" data-key="d"><span class="label">E<br>(D)</span></div> <div class="key white" data-note="F4" data-key="f"><span class="label">F<br>(F)</span></div> <div class="key black" data-note="F#4" data-key="t"><span class="label">F#<br>(T)</span></div> <div class="key white" data-note="G4" data-key="g"><span class="label">G<br>(G)</span></div> <div class="key black" data-note="G#4" data-key="y"><span class="label">G#<br>(Y)</span></div> <div class="key white" data-note="A4" data-key="h"><span class="label">A<br>(H)</span></div> <div class="key black" data-note="A#4" data-key="u"><span class="label">A#<br>(U)</span></div> <div class="key white" data-note="B4" data-key="j"><span class="label">B<br>(J)</span></div> <div class="key white" data-note="C5" data-key="k"><span class="label">C<br>(K)</span></div> </div> <div id="controls"> <p>Play with mouse or keys <kbd>A</kbd> through <kbd>K</kbd></p> <p>Synthesis: Triangle Oscillator | ADSR: 0.1, 0.2, 0.5, 1.0</p> </div> <script> const synth = new Tone.PolySynth(Tone.Synth, { oscillator: { type: "triangle" }, envelope: { attack: 0.1, decay: 0.2, sustain: 0.5, release: 1 } }).toDestination(); const keys = document.querySelectorAll('.key'); const startOverlay = document.getElementById('start-overlay'); const pressedNotes = new Set(); const keyMap = {}; keys.forEach(key => { keyMap[key.getAttribute('data-key')] = { note: key.getAttribute('data-note'), element: key }; }); document.getElementById('start-button').addEventListener('click', async () => { await Tone.start(); startOverlay.style.display = 'none'; }); function playNote(note, element) { if (!pressedNotes.has(note)) { synth.triggerAttack(note); pressedNotes.add(note); element.classList.add('active'); } } function releaseNote(note, element) { if (pressedNotes.has(note)) { synth.triggerRelease(note); pressedNotes.delete(note); element.classList.remove('active'); } } keys.forEach(key => { const note = key.getAttribute('data-note'); key.addEventListener('mousedown', (e) => { e.preventDefault(); playNote(note, key); }); key.addEventListener('mouseup', () => releaseNote(note, key)); key.addEventListener('mouseleave', () => releaseNote(note, key)); key.addEventListener('touchstart', (e) => { e.preventDefault(); playNote(note, key); }); key.addEventListener('touchend', () => releaseNote(note, key)); }); window.addEventListener('keydown', (e) => { if (e.repeat) return; const entry = keyMap[e.key.toLowerCase()]; if (entry) playNote(entry.note, entry.element); }); window.addEventListener('keyup', (e) => { const entry = keyMap[e.key.toLowerCase()]; if (entry) releaseNote(entry.note, entry.element); }); </script> </body> </html> ```

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

modelgemini-3-flash-preview
vendorGoogle
harnesspi-agent
generated2026-07-22

EVALUATION

4/5 working

Piano keyboard appears after clicking start; keys highlight and labels show mouse/keyboard mapping, arrows/wasd/enter not tested for notes.

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.

idle motion0.0%
on click99.8%
on drag0.0%
on wheel0.0%
on enter + space0.0%
on arrow keys0.0%
on w/a/s/d0.0%
frame spread48.1 / 255
console errors0
js errors none

METRICS

runtime37.7s
tokens in7.2k
tokens out5.9k
cached0
cost / run$0.0214