This commit is contained in:
pavel 2026-03-02 20:09:22 +01:00
commit 8ca3b27483
3 changed files with 2083 additions and 0 deletions

140
index.html Normal file
View file

@ -0,0 +1,140 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Letterboard Synth</title>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@400;500;700&family=IBM+Plex+Mono:wght@400;500&display=swap" rel="stylesheet">
<link rel="stylesheet" href="./styles.css">
</head>
<body>
<div class="backdrop" aria-hidden="true">
<div class="backdrop__orb backdrop__orb--left"></div>
<div class="backdrop__orb backdrop__orb--right"></div>
<div class="backdrop__grid"></div>
</div>
<main class="app">
<section class="hero">
<p class="eyebrow">Browser instrument</p>
<h1>Play your laptop keyboard like a synth.</h1>
<p class="hero__copy">
Letters map to notes in a friendly scale, so you can improvise fast.
Switch textures, shift octaves, and hold multiple keys at once.
</p>
</section>
<section class="controls" aria-label="Instrument controls">
<button class="power" id="audio-toggle" type="button">Start audio</button>
<label class="control-card">
<span class="control-card__label">Texture</span>
<select id="texture-select" aria-label="Select sound texture">
<option value="piano">Piano</option>
<option value="warm">Warm Keys</option>
<option value="glass">Glass Pad</option>
<option value="pluck">Pluck</option>
<option value="organ">Tape Organ</option>
</select>
</label>
<div class="control-card">
<span class="control-card__label">Mapping</span>
<div class="mapping-grid">
<label class="mini-field" for="scale-select">
<span>Scale</span>
<select id="scale-select" aria-label="Select scale">
<option value="major">Major</option>
<option value="minor">Minor</option>
<option value="dorian">Dorian</option>
<option value="pentatonic">Pentatonic</option>
<option value="chromatic">Chromatic</option>
</select>
</label>
<label class="mini-field" for="layout-select">
<span>Layout</span>
<select id="layout-select" aria-label="Select keyboard layout">
<option value="cascade">Cascade</option>
<option value="linear">Linear</option>
<option value="grid">Grid</option>
</select>
</label>
</div>
</div>
<div class="control-card control-card--octave">
<span class="control-card__label">Octave</span>
<div class="octave-controls">
<button id="octave-down" type="button" aria-label="Lower octave">-</button>
<output id="octave-display" aria-live="polite">4</output>
<button id="octave-up" type="button" aria-label="Raise octave">+</button>
</div>
</div>
<div class="control-card">
<span class="control-card__label">Metronome</span>
<div class="metronome-controls">
<button id="metronome-toggle" type="button" class="metronome-toggle">Start</button>
<div class="metronome-status">
<span id="metronome-light" class="metronome-light" aria-hidden="true"></span>
<span id="tempo-display">110 BPM</span>
</div>
</div>
<label class="tempo-control" for="tempo-slider">
<span>Tempo</span>
<input id="tempo-slider" type="range" min="60" max="180" step="1" value="110" aria-label="Metronome tempo">
</label>
</div>
<div class="control-card">
<span class="control-card__label">Now playing</span>
<p id="note-display" class="note-display">Press any mapped letter</p>
</div>
</section>
<section class="board" aria-label="Keyboard mapping">
<div class="board-row" id="row-top"></div>
<div class="board-row board-row--offset" id="row-middle"></div>
<div class="board-row board-row--wide" id="row-bottom"></div>
</section>
<section class="sequencer" aria-label="Beat sequencer">
<div class="sequencer__header">
<div>
<p class="eyebrow">Beat sequencer</p>
<h2>Build a groove under your keys.</h2>
<p class="sequencer__copy">
Toggle steps to program drums. The pattern follows the same tempo as the metronome.
</p>
</div>
<div class="sequencer__transport">
<button id="sequencer-toggle" type="button" class="sequencer-button">Play beat</button>
<button id="sequencer-clear" type="button" class="sequencer-button sequencer-button--ghost">Clear</button>
<button id="sequencer-random" type="button" class="sequencer-button sequencer-button--ghost">Randomize</button>
</div>
</div>
<div id="sequencer-grid" class="sequencer-grid"></div>
</section>
<section class="legend">
<div class="legend__item">
<span class="legend__title">Rows</span>
<p><code>QWERTYUIOP</code>, <code>ASDFGHJKL</code>, and <code>ZXCVBNM</code> are all mapped.</p>
</div>
<div class="legend__item">
<span class="legend__title">Texture</span>
<p>Swap between mellow, airy, percussive, and sustained synth voices.</p>
</div>
<div class="legend__item">
<span class="legend__title">Tip</span>
<p>Hold several letters together for chords. The board shows live <code>R</code>, <code>3</code>, <code>5</code> overlays for the current shape.</p>
</div>
</section>
</main>
<script src="./app.js"></script>
</body>
</html>