Liveweave: Generative AI Web Editor & HTML/CSS/JS Playground
Initializing
Liveweave
Web
expand_more
home
Home
Palette
Color Explorer
arrow_outward
Polyline
Graphics Editor
arrow_outward
frame_source
Python Playground
arrow_outward
build
Tools
expand_more
restart_alt
Load "Hello Weaver!"
post_add
Generate Lorem ipsum...
code
Format HTML
code_blocks
Format CSS
data_object
Format JavaScript
library_add
Library
expand_more
A
Algolia JS
Animate CSS
Apex Charts JS
B
Bulma CSS
Bootstrap
C
Chart JS
Chartist
Create JS
D
D3
Dojo
F
Foundation
Fullpage JS
G
Granim JS
Google Charts
H
Halfmoon
J
jQuery
M
Materialize
Moment JS
Masonry JS
Milligram CSS
P
Pure CSS
Primer CSS
Popper JS
Pattern CSS
Picnic CSS
R
React JS
Raphael JS
Raisin CSS
S
Semantic UI
Skeleton CSS
Spectre CSS
Tachyons CSS
T
Tailwind
Three JS
U
UI Kit
Vis JS
W
Water CSS
download
Download
expand_more
developer_mode
Download as HTML
folder_zip
Download as .ZIP
cloud_upload
Fork
account_circle
Login
settings
Settings
expand_more
Editor font
14
px
A
A
Night mode
Line number
Mini map
Word wrap
sync_alt
Reset Settings
smart_display
Run
left_panel_close
AI Assistant
dashboard
All
HTML
CSS
JS
visibility
Preview
bolt
Live Preview
terminal
JS Console
<!DOCTYPE html> <html> <head> <title>Mini synthesizer</title> </head> <body> <!-- Start your code here --> <div class="synth"> <header> <div> <h1><span>≋</span> MINI SYNTH</h1> <p>PLAY. EXPERIMENT. CREATE.</p> </div> <span id="status">READY</span> </header> <div class="controls"> <label> WAVEFORM <select id="wave"> <option value="sine">Sine</option> <option value="triangle">Triangle</option> <option value="square">Square</option> <option value="sawtooth">Sawtooth</option> </select> </label> <label> VOLUME <input id="volume" type="range" min="0" max="100" value="35"> </label> <div class="octave"> <span>OCTAVE</span> <div> <button id="minus" type="button">-</button> <strong id="octave">4</strong> <button id="plus" type="button">+</button> </div> </div> </div> <div id="piano" class="piano"></div> <footer> COMPUTER KEYS: A W S E D F T G Y H U J K </footer> </div> <!-- End your code here --> </body> </html>
* { box-sizing: border-box; } body { margin: 0; min-height: 100vh; background: #0b1020; font-family: Arial, sans-serif; display: flex; justify-content: center; align-items: center; padding: 20px; } .synth { width: 100%; max-width: 760px; padding: 28px; background: #172033; color: #f1f5f9; border: 1px solid #334155; border-radius: 16px; box-shadow: 0 20px 50px #0005; } header { display: flex; justify-content: space-between; align-items: center; gap: 12px; margin-bottom: 30px; } h1 { margin: 0; font-size: 25px; letter-spacing: 2px; } h1 span { color: #64e5ce; } header p, footer { font-size: 11px; color: #94a3b8; letter-spacing: 1px; } #status { color: #64e5ce; font-size: 11px; } .controls { display: flex; align-items: end; gap: 24px; flex-wrap: wrap; margin-bottom: 28px; } .controls label { flex: 1; min-width: 130px; font-size: 11px; color: #94a3b8; } select, button { background: #29364c; color: white; border: 1px solid #475569; border-radius: 7px; padding: 10px; cursor: pointer; } select { display: block; width: 100%; margin-top: 10px; } input[type="range"] { display: block; width: 100%; margin-top: 18px; accent-color: #64e5ce; } .octave { font-size: 11px; color: #94a3b8; } .octave > div { display: flex; align-items: center; gap: 14px; margin-top: 10px; } .octave strong { color: #64e5ce; font-size: 16px; } .piano { display: flex; position: relative; height: 160px; width: 100%; touch-action: none; user-select: none; } .white { flex: 1; position: relative; background: linear-gradient(#fff, #d5dce5); border: 1px solid #8995a5; border-radius: 0 0 5px 5px; cursor: pointer; touch-action: none; } .black { position: absolute; top: 0; height: 98px; width: 9%; background: linear-gradient(#354052, #080b11); border: 1px solid #05070c; border-radius: 0 0 4px 4px; z-index: 2; cursor: pointer; touch-action: none; } .white.active { background: #64e5ce; } .black.active { background: #249f90; } .key-label { position: absolute; bottom: 12px; left: 0; right: 0; text-align: center; font-size: 10px; color: #64748b; pointer-events: none; } footer { margin-top: 22px; text-align: center; line-height: 1.8; } @media (max-width: 520px) { .synth { padding: 16px; } h1 { font-size: 19px; } .controls { gap: 12px; } .piano { height: 120px; } .black { height: 75px; } }
(function () { const piano = document.getElementById("piano"); const wave = document.getElementById("wave"); const volume = document.getElementById("volume"); const status = document.getElementById("status"); const octaveLabel = document.getElementById("octave"); let audio = null; let octave = 4; const voices = new Map(); const pressedKeys = new Map(); const noteNames = [ "C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B" ]; const keyboard = "awsedftgyhujk"; const blackNotes = [1, 3, 6, 8, 10]; function midiNote(index) { return 12 * (octave + 1) + index; } function isBlack(index) { return blackNotes.includes(index % 12); } function frequency(note) { return 440 * Math.pow(2, (note - 69) / 12); } function startNote(note) { if (voices.has(note)) return; try { if (!audio) { const AudioClass = window.AudioContext || window.webkitAudioContext; audio = new AudioClass(); } audio.resume(); const oscillator = audio.createOscillator(); const gain = audio.createGain(); const now = audio.currentTime; oscillator.type = wave.value; oscillator.frequency.value = frequency(note); gain.gain.setValueAtTime(0, now); gain.gain.linearRampToValueAtTime( Number(volume.value) / 100 * 0.25, now + 0.015 ); oscillator.connect(gain); gain.connect(audio.destination); oscillator.start(); voices.set(note, { oscillator, gain }); status.textContent = "PLAYING"; updateKeys(); } catch (error) { status.textContent = "AUDIO UNAVAILABLE"; } } function stopNote(note) { const voice = voices.get(note); if (!voice || !audio) return; const now = audio.currentTime; voice.gain.gain.cancelScheduledValues(now); voice.gain.gain.setTargetAtTime( 0.00001, now, 0.025 ); voice.oscillator.stop(now + 0.2); voices.delete(note); status.textContent = voices.size > 0 ? "PLAYING" : "READY"; updateKeys(); } function stopAll() { for (const note of Array.from(voices.keys())) { stopNote(note); } } function updateKeys() { piano.querySelectorAll("[data-note]").forEach(function (key) { const note = Number(key.dataset.note); key.classList.toggle("active", voices.has(note)); }); } function createKey(index, black) { const key = document.createElement("div"); key.className = black ? "black" : "white"; key.dataset.note = midiNote(index); if (!black) { const label = document.createElement("span"); label.className = "key-label"; label.textContent = noteNames[index % 12]; key.appendChild(label); } key.addEventListener("pointerdown", function (event) { event.preventDefault(); key.setPointerCapture(event.pointerId); startNote(Number(key.dataset.note)); }); key.addEventListener("pointerup", function () { stopNote(Number(key.dataset.note)); }); key.addEventListener("pointercancel", function () { stopNote(Number(key.dataset.note)); }); key.addEventListener("lostpointercapture", function () { stopNote(Number(key.dataset.note)); }); return key; } function renderKeyboard() { piano.replaceChildren(); // Eight white keys, from C to the next C. for (let i = 0; i <= 12; i++) { if (isBlack(i)) continue; piano.appendChild(createKey(i, false)); } // Five black keys. for (let i = 0; i <= 12; i++) { if (!isBlack(i)) continue; const key = createKey(i, true); let whiteBefore = 0; for (let j = 0; j < i; j++) { if (!isBlack(j)) whiteBefore++; } key.style.left = "calc(" + (whiteBefore / 8 * 100) + "% - 4.5%)"; piano.appendChild(key); } octaveLabel.textContent = octave; } document.getElementById("minus").addEventListener( "click", function () { stopAll(); pressedKeys.clear(); octave = Math.max(2, octave - 1); renderKeyboard(); } ); document.getElementById("plus").addEventListener( "click", function () { stopAll(); pressedKeys.clear(); octave = Math.min(6, octave + 1); renderKeyboard(); } ); document.addEventListener("keydown", function (event) { if (event.repeat || event.ctrlKey || event.metaKey || event.altKey) return; const tag = document.activeElement.tagName; if (["INPUT", "SELECT", "TEXTAREA"].includes(tag)) { return; } const key = event.key.toLowerCase(); const index = keyboard.indexOf(key); if (index === -1 || pressedKeys.has(key)) return; const note = midiNote(index); pressedKeys.set(key, note); startNote(note); }); document.addEventListener("keyup", function (event) { const key = event.key.toLowerCase(); if (!pressedKeys.has(key)) return; stopNote(pressedKeys.get(key)); pressedKeys.delete(key); }); window.addEventListener("blur", function () { pressedKeys.clear(); stopAll(); }); renderKeyboard(); })();
terminal
JavaScript Console
Preview
Clear
close
›
Run