Music Medium
Clausic — Rock · Pop · Ambient
6 original MIDI compositions across 3 genres — Rock (Neon City Riff, Desert Storm), Pop (Summer Signal, Midnight Drive), Ambient (Glass Forest, Ocean Drift) with piano-roll visualizer.
Open in Lab
MCP
html-midi-player tone-js magenta
Targets: JS HTML
Code
:root {
--bg: #0f0f14;
--surface: #1a1a24;
--accent: #7c6af7;
--accent-dim: rgba(124, 106, 247, 0.15);
--text: #e0dff5;
--muted: #6b6a8a;
--radius: 10px;
}
*,
*::before,
*::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
body {
background: var(--bg);
color: var(--text);
font-family: "Inter", system-ui, -apple-system, sans-serif;
max-width: 900px;
margin: 0 auto;
padding: 3rem 1.5rem 5rem;
min-height: 100vh;
}
header {
margin-bottom: 2rem;
}
header h1 {
font-size: 2.6rem;
font-weight: 700;
letter-spacing: -0.04em;
background: linear-gradient(135deg, var(--text), var(--accent));
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
line-height: 1.1;
}
.subtitle {
color: var(--muted);
font-size: 0.95rem;
margin-top: 0.4rem;
}
.tabs {
display: flex;
gap: 0.4rem;
margin-bottom: 1.8rem;
flex-wrap: wrap;
}
.tab {
background: var(--surface);
border: 1px solid transparent;
color: var(--muted);
padding: 0.45rem 1.1rem;
border-radius: 999px;
cursor: pointer;
font-size: 0.88rem;
font-weight: 500;
font-family: inherit;
transition: background 0.15s, color 0.15s;
}
.tab:hover {
color: var(--text);
border-color: var(--accent-dim);
}
.tab.active {
background: var(--accent);
color: #fff;
border-color: var(--accent);
}
.piece {
display: none;
}
.piece.active {
display: block;
animation: fadeIn 0.22s ease;
}
.piece h2 {
font-size: 1.35rem;
font-weight: 600;
letter-spacing: -0.02em;
margin-bottom: 0.3rem;
display: flex;
align-items: center;
gap: 0.6rem;
}
.meta {
font-family: "SF Mono", "Fira Mono", "Consolas", monospace;
font-size: 0.8rem;
color: var(--muted);
margin-bottom: 1.4rem;
letter-spacing: 0.02em;
}
.gen-badge {
display: inline-flex;
align-items: center;
justify-content: center;
width: 2rem;
height: 2rem;
border-radius: 50%;
background: #e05252;
color: #fff;
font-size: 0.78rem;
font-weight: 700;
flex-shrink: 0;
}
midi-visualizer {
display: block;
width: 100%;
height: 280px;
background: var(--surface);
border-radius: var(--radius);
margin-bottom: 0.8rem;
overflow: hidden;
border: 1px solid rgba(255, 255, 255, 0.04);
}
midi-visualizer svg {
width: 100%;
height: 100%;
}
midi-player {
display: block;
width: 100%;
--player-background: var(--surface);
--player-color: var(--accent);
--player-playing-color: var(--accent);
border-radius: var(--radius);
overflow: hidden;
border: 1px solid rgba(255, 255, 255, 0.04);
margin-bottom: 1.2rem;
}
/* Generation code details */
.gen-details {
margin-top: 0.4rem;
margin-bottom: 1.2rem;
}
.gen-details summary {
cursor: pointer;
font-size: 0.82rem;
font-weight: 500;
color: var(--accent);
padding: 0.4rem 0;
user-select: none;
display: inline-flex;
align-items: center;
gap: 0.4rem;
list-style: none;
}
.gen-details summary::-webkit-details-marker {
display: none;
}
.gen-details summary::before {
content: "\25B6";
font-size: 0.6rem;
transition: transform 0.2s ease;
display: inline-block;
}
.gen-details[open] summary::before {
transform: rotate(90deg);
}
.gen-details summary:hover {
color: var(--text);
}
.gen-code {
background: var(--surface);
border: 1px solid rgba(255, 255, 255, 0.06);
border-radius: var(--radius);
padding: 1rem 1.2rem;
margin-top: 0.5rem;
overflow-x: auto;
}
.gen-code pre {
font-family: "SF Mono", "Fira Mono", "Consolas", monospace;
font-size: 0.75rem;
line-height: 1.6;
color: var(--text);
white-space: pre-wrap;
word-wrap: break-word;
}
.gen-code .comment {
color: var(--muted);
}
.gen-code .key {
color: #7dd3fc;
}
.gen-code .val {
color: #a5f3c4;
}
.gen-code .str {
color: #fcd34d;
}
@keyframes fadeIn {
from {
opacity: 0;
transform: translateY(7px);
}
to {
opacity: 1;
transform: translateY(0);
}
}/* ====================================================================
Clausic — Rock · Pop · Ambient
6 procedurally-generated NoteSequence compositions for html-midi-player
==================================================================== */
// --------------- Scale & Theory Helpers ---------------
const SCALES = {
blues: [0, 3, 5, 6, 7, 10], // 1 b3 4 b5 5 b7
minor: [0, 2, 3, 5, 7, 8, 10], // natural minor
major: [0, 2, 4, 5, 7, 9, 11],
pentatonic: [0, 2, 4, 7, 9], // major pentatonic
lydian: [0, 2, 4, 6, 7, 9, 11], // major with #4
};
const ROOTS = { C: 60, D: 62, E: 64, F: 65, "F#": 66, G: 67, A: 69, Bb: 70, B: 71 };
/** Map a scale degree (0-based, can exceed one octave) to a MIDI pitch */
function scaleNote(root, scale, degree) {
if (degree == null) return null;
const len = scale.length;
const octave = Math.floor(degree / len);
const idx = ((degree % len) + len) % len;
return root + scale[idx] + octave * 12;
}
/** Convert beat number to seconds */
function beatToSec(beat, bpm) {
return (beat * 60) / bpm;
}
// --------------- NoteSequence Generator ---------------
function generate(cfg) {
const { root, scale, bpm, bars, chords, melody, bass, drums, programs, arpeggios } = cfg;
const notes = [];
const sc = SCALES[scale];
const beatDur = 60 / bpm; // duration of one quarter note in seconds
const eighthDur = beatDur / 2; // duration of one eighth note
const beatsPerBar = 4;
const eighthsPerBar = 8;
const totalBeats = bars * beatsPerBar;
const totalTime = totalBeats * beatDur;
// ---- Chords / Pads ----
if (chords && chords.length > 0) {
for (let bar = 0; bar < bars; bar++) {
const chord = chords[bar % chords.length];
const startBeat = bar * beatsPerBar;
const start = beatToSec(startBeat, bpm);
const end = beatToSec(startBeat + beatsPerBar, bpm);
chord.forEach((deg) => {
const pitch = scaleNote(root, sc, deg);
if (pitch != null) {
notes.push({
pitch,
startTime: start,
endTime: end,
velocity: 50 + Math.floor(Math.random() * 15),
program: programs.pad,
});
}
});
}
}
// ---- Arpeggios (for ambient tracks) ----
if (arpeggios && arpeggios.length > 0) {
const arpRoot = root - 12; // one octave lower for arps base, or use provided
let arpIdx = 0;
for (let bar = 0; bar < bars; bar++) {
const chord = chords ? chords[bar % chords.length] : [0, 2, 4];
for (let eighth = 0; eighth < eighthsPerBar; eighth++) {
const arpDeg = arpeggios[arpIdx % arpeggios.length];
arpIdx++;
if (arpDeg == null) continue;
// Map arp degree into the current chord
const chordIdx = ((arpDeg % chord.length) + chord.length) % chord.length;
const octShift = Math.floor(arpDeg / chord.length);
const deg = chord[chordIdx] + octShift * sc.length;
const pitch = scaleNote(root, sc, deg);
if (pitch == null) continue;
const startBeat = bar * beatsPerBar + eighth * 0.5;
const start = beatToSec(startBeat, bpm);
const dur = eighthDur * (1.5 + Math.random() * 1.5); // longer, overlapping notes
notes.push({
pitch,
startTime: start,
endTime: start + dur,
velocity: 35 + Math.floor(Math.random() * 20),
program: programs.melody,
});
}
}
}
// ---- Melody ----
if (melody && melody.length > 0) {
let melIdx = 0;
for (let bar = 0; bar < bars; bar++) {
for (let eighth = 0; eighth < eighthsPerBar; eighth++) {
const deg = melody[melIdx % melody.length];
melIdx++;
if (deg == null) continue;
const pitch = scaleNote(root, sc, deg);
if (pitch == null) continue;
const startBeat = bar * beatsPerBar + eighth * 0.5;
const start = beatToSec(startBeat, bpm);
// Vary note lengths: some eighth, some quarter
const isLong = eighth % 2 === 0 && melody[melIdx % melody.length] == null;
const dur = isLong ? beatDur : eighthDur * 0.9;
notes.push({
pitch,
startTime: start,
endTime: start + dur,
velocity: 70 + Math.floor(Math.random() * 25),
program: programs.melody,
});
}
}
}
// ---- Bass ----
if (bass && bass.length > 0) {
let bassIdx = 0;
for (let bar = 0; bar < bars; bar++) {
for (let beat = 0; beat < beatsPerBar; beat++) {
const deg = bass[bassIdx % bass.length];
bassIdx++;
if (deg == null) continue;
const pitch = scaleNote(root - 24, sc, deg); // two octaves below root
if (pitch == null) continue;
const startBeat = bar * beatsPerBar + beat;
const start = beatToSec(startBeat, bpm);
const dur = beatDur * 0.85;
notes.push({
pitch,
startTime: start,
endTime: start + dur,
velocity: 75 + Math.floor(Math.random() * 20),
program: programs.bass,
});
}
}
}
// ---- Drums ----
if (drums) {
const drumMap = { kick: 36, snare: 38, hat: 42, openHat: 46, crash: 49, ride: 51 };
for (const [name, pattern] of Object.entries(drums)) {
if (!pattern || pattern.length === 0) continue;
const drumPitch = drumMap[name] || 38;
let patIdx = 0;
for (let bar = 0; bar < bars; bar++) {
for (let eighth = 0; eighth < eighthsPerBar; eighth++) {
const hit = pattern[patIdx % pattern.length];
patIdx++;
if (!hit) continue;
const startBeat = bar * beatsPerBar + eighth * 0.5;
const start = beatToSec(startBeat, bpm);
const vel = typeof hit === "number" ? hit : 70 + Math.floor(Math.random() * 25);
notes.push({
pitch: drumPitch,
startTime: start,
endTime: start + 0.15,
velocity: vel,
isDrum: true,
});
}
}
}
}
return {
notes,
tempos: [{ time: 0, qpm: bpm }],
totalTime,
};
}
// --------------- Track Configurations ---------------
const tracks = [
// ── 0: Neon City Riff — Rock · E blues · 132 BPM ──
{
root: ROOTS.E,
scale: "blues",
bpm: 132,
bars: 16,
// E blues power chords: E5, G5, A5, Bb5 (i, bIII, IV, bV)
chords: [
[0, 4, 6], // E power (root, 5th, octave in blues scale degrees)
[0, 4, 6], // E power again
[2, 4, 8], // A power
[1, 3, 7], // G power
],
// Bluesy melody: mix of blues licks with rests
melody: [
4,
5,
6,
null,
4,
3,
2,
null,
0,
2,
3,
4,
null,
null,
5,
4,
3,
2,
0,
null,
2,
3,
4,
5,
6,
null,
5,
4,
3,
null,
2,
0,
4,
4,
5,
6,
null,
8,
7,
6,
5,
null,
4,
3,
2,
3,
4,
null,
0,
null,
2,
3,
5,
4,
3,
2,
0,
null,
null,
null,
2,
4,
5,
6,
],
// Bass follows chord roots with walking passing tones
bass: [
0,
null,
2,
0,
0,
4,
2,
null,
2,
null,
4,
2,
1,
null,
0,
1,
0,
2,
0,
null,
0,
null,
4,
3,
2,
4,
2,
null,
1,
0,
1,
null,
],
// Driving rock drums
drums: {
kick: [1, 0, 0, 0, 1, 0, 1, 0],
snare: [0, 0, 1, 0, 0, 0, 1, 0],
hat: [1, 1, 1, 1, 1, 1, 1, 1],
crash: [
1,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0, // crash on bar 1 beat 1
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
1,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
],
},
programs: { melody: 30, bass: 34, pad: 29 }, // DistortionGuitar, ElectricBassPick, OverdrivenGuitar
},
// ── 1: Desert Storm — Rock · D minor · 92 BPM ──
{
root: ROOTS.D,
scale: "minor",
bpm: 92,
bars: 16,
// Dm, Bb, C, Am (i, bVI, bVII, v)
chords: [
[0, 2, 4], // Dm (D F A)
[5, 7, 9], // Bb
[6, 8, 10], // C
[4, 6, 8], // Am
],
// Moody, angular minor melody with tremolo feel
melody: [
7,
null,
6,
5,
4,
null,
3,
2,
0,
null,
null,
2,
4,
5,
null,
4,
6,
7,
null,
6,
5,
4,
null,
2,
3,
null,
4,
null,
2,
0,
null,
null,
7,
8,
9,
null,
7,
6,
5,
null,
4,
null,
2,
3,
4,
null,
6,
5,
4,
3,
2,
null,
0,
null,
2,
4,
5,
null,
null,
4,
3,
2,
0,
null,
],
bass: [
0,
null,
0,
2,
5,
null,
5,
4,
6,
null,
6,
4,
4,
null,
2,
0,
0,
2,
0,
null,
5,
null,
6,
5,
6,
4,
6,
null,
4,
2,
0,
null,
],
// Heavy, half-time feel drums
drums: {
kick: [1, 0, 0, 0, 0, 0, 1, 0],
snare: [0, 0, 0, 0, 1, 0, 0, 0],
hat: [1, 0, 1, 0, 1, 0, 1, 0],
ride: [0, 1, 0, 1, 0, 1, 0, 1],
crash: [
1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0,
],
},
programs: { melody: 29, bass: 33, pad: 48 }, // OverdrivenGuitar, ElectricBass, StringEnsemble
},
// ── 2: Summer Signal — Pop · C major · 118 BPM ──
{
root: ROOTS.C,
scale: "major",
bpm: 118,
bars: 16,
// C, G, Am, F (I, V, vi, IV) — classic pop
chords: [
[0, 2, 4], // C (C E G)
[4, 6, 8], // G
[5, 7, 9], // Am
[3, 5, 7], // F
],
// Bright, catchy pop melody
melody: [
4,
5,
7,
null,
7,
5,
4,
null,
2,
4,
5,
7,
null,
9,
7,
5,
4,
null,
5,
4,
2,
null,
0,
2,
4,
null,
null,
null,
2,
4,
5,
null,
7,
9,
7,
5,
4,
null,
5,
7,
9,
null,
11,
9,
7,
null,
5,
4,
5,
7,
5,
4,
2,
null,
4,
null,
7,
null,
5,
null,
4,
2,
0,
null,
],
bass: [
0,
null,
0,
2,
4,
null,
4,
2,
5,
null,
5,
4,
3,
null,
3,
2,
0,
2,
0,
null,
4,
null,
2,
4,
5,
4,
5,
null,
3,
null,
2,
0,
],
// Upbeat pop drums with clap-style snare
drums: {
kick: [1, 0, 0, 1, 1, 0, 0, 0],
snare: [0, 0, 1, 0, 0, 0, 1, 0],
hat: [1, 1, 1, 1, 1, 1, 1, 1],
openHat: [0, 0, 0, 0, 0, 0, 0, 1],
},
programs: { melody: 80, bass: 38, pad: 4 }, // SynthLeadSquare, SynthBass, ElectricPiano
},
// ── 3: Midnight Drive — Pop · A minor · 96 BPM ──
{
root: ROOTS.A,
scale: "minor",
bpm: 96,
bars: 16,
// Am, F, C, G (i, bVI, bIII, bVII)
chords: [
[0, 2, 4], // Am
[5, 7, 9], // F
[2, 4, 6], // C
[6, 8, 10], // G
],
// Moody, smooth pop melody with longer phrases
melody: [
4,
null,
5,
4,
2,
null,
null,
null,
0,
2,
4,
null,
5,
6,
5,
null,
4,
null,
2,
null,
4,
5,
7,
null,
6,
5,
4,
null,
2,
null,
null,
null,
7,
null,
8,
7,
5,
4,
null,
2,
4,
null,
null,
5,
7,
null,
9,
7,
5,
null,
4,
2,
0,
null,
2,
null,
4,
null,
5,
null,
4,
2,
0,
null,
],
bass: [
0,
null,
0,
null,
5,
null,
5,
4,
2,
null,
2,
4,
6,
null,
4,
2,
0,
null,
2,
0,
5,
null,
4,
5,
2,
null,
4,
2,
6,
null,
4,
null,
],
drums: {
kick: [1, 0, 0, 0, 0, 0, 1, 0],
snare: [0, 0, 1, 0, 0, 1, 0, 0],
hat: [1, 0, 1, 1, 1, 0, 1, 1],
ride: [0, 1, 0, 0, 0, 1, 0, 0],
},
programs: { melody: 4, bass: 33, pad: 89 }, // ElectricPiano, ElectricBass, PadWarm
},
// ── 4: Glass Forest — Ambient · F# pentatonic · 52 BPM ──
{
root: ROOTS["F#"],
scale: "pentatonic",
bpm: 52,
bars: 16,
// Wide open voicings cycling through pentatonic clusters
chords: [
[0, 2, 4], // F# A# C#
[1, 3, 5], // G# C# E#
[2, 4, 6], // A# E# G#
[0, 3, 5], // F# C# E#
],
// No standard melody — use arpeggios instead
melody: null,
// Slow crystalline arpeggios
arpeggios: [
0,
null,
1,
null,
2,
null,
3,
null,
2,
null,
1,
null,
0,
null,
null,
null,
1,
null,
2,
null,
3,
null,
4,
null,
3,
null,
2,
null,
1,
null,
null,
null,
0,
null,
null,
2,
null,
3,
null,
4,
null,
3,
null,
2,
null,
1,
null,
null,
2,
null,
3,
null,
5,
null,
4,
null,
3,
null,
null,
2,
null,
1,
null,
null,
],
bass: [0, null, null, null, 1, null, null, null, 2, null, null, null, 0, null, null, null],
drums: null, // No drums for ambient
programs: { melody: 11, bass: 88, pad: 89 }, // Vibraphone, PadNewAge, PadWarm
},
// ── 5: Ocean Drift — Ambient · C Lydian · 44 BPM ──
{
root: ROOTS.C,
scale: "lydian",
bpm: 44,
bars: 16,
// Lydian chord voicings: Cmaj7, D/C, Em, F#dim/C
chords: [
[0, 2, 4, 6], // C E G B (Cmaj7)
[1, 3, 5, 7], // D F# A C
[2, 4, 6], // E G B
[0, 3, 6], // C F# B (lydian tension)
],
melody: null,
// Slow oceanic arpeggios
arpeggios: [
0,
null,
null,
1,
null,
null,
2,
null,
null,
3,
null,
null,
2,
null,
null,
1,
null,
null,
0,
null,
null,
1,
null,
2,
null,
3,
null,
null,
null,
2,
null,
null,
0,
null,
2,
null,
null,
3,
null,
null,
4,
null,
null,
3,
null,
2,
null,
null,
1,
null,
null,
null,
0,
null,
null,
1,
null,
2,
null,
null,
null,
1,
null,
null,
],
bass: [0, null, null, null, 1, null, null, null, 2, null, null, null, 0, null, null, null],
drums: null, // No drums for ambient
programs: { melody: 88, bass: 48, pad: 89 }, // PadNewAge, StringEnsemble, PadWarm
},
];
// --------------- Visualization & Tab Helpers ---------------
function stopAllPlayers() {
document.querySelectorAll("midi-player").forEach((p) => {
try {
p.stop();
} catch (_) {}
});
}
function zoomVisualizer(viz) {
const svg = viz.querySelector("svg");
if (!svg) return;
const rects = svg.querySelectorAll("rect[data-note]");
if (!rects.length) return;
let minY = Infinity,
maxY = -Infinity;
rects.forEach((r) => {
const y = parseFloat(r.getAttribute("y") || 0),
h = parseFloat(r.getAttribute("height") || 0);
if (y < minY) minY = y;
if (y + h > maxY) maxY = y + h;
});
if (!isFinite(minY)) return;
const vb = svg.getAttribute("viewBox");
if (!vb) return;
const [vx, , vw, vh] = vb.split(" ").map(Number);
const m = vh * 0.05;
svg.setAttribute(
"viewBox",
`${vx} ${Math.max(0, minY - m)} ${vw} ${Math.min(vh, maxY - minY + m * 2)}`
);
svg.setAttribute("preserveAspectRatio", "xMidYMid meet");
}
function observeViz(viz) {
const obs = new MutationObserver(() => {
const svg = viz.querySelector("svg");
if (svg && svg.querySelectorAll("rect[data-note]").length > 0) {
zoomVisualizer(viz);
obs.disconnect();
}
});
obs.observe(viz, { childList: true, subtree: true, attributes: true });
zoomVisualizer(viz);
}
// --------------- Initialization ---------------
document.addEventListener("DOMContentLoaded", () => {
// Generate and assign NoteSequences to each player/visualizer
tracks.forEach((cfg, i) => {
const ns = generate(cfg);
const player = document.querySelector(`#piece-new-${i} midi-player`);
const viz = document.querySelector(`#viz-${i}`);
if (viz) {
viz.noteSequence = ns;
}
if (player) {
player.noteSequence = ns;
}
});
// Observe all visualizers for zoom
document.querySelectorAll("midi-visualizer").forEach(observeViz);
// Tab switching
document.querySelectorAll(".tabs").forEach((nav) => {
const tabs = nav.querySelectorAll(".tab");
tabs.forEach((tab) => {
tab.addEventListener("click", () => {
stopAllPlayers();
tabs.forEach((t) => t.classList.remove("active"));
tab.classList.add("active");
const parent = tab.closest(".collection") || document.body;
parent.querySelectorAll(".piece").forEach((p) => p.classList.remove("active"));
const target = document.getElementById(`piece-${tab.dataset.track}`);
if (target) {
target.classList.add("active");
target.querySelectorAll("midi-visualizer").forEach(observeViz);
}
});
});
});
});<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Clausic — Rock · Pop · Ambient</title>
<link rel="stylesheet" href="style.css">
<script src="https://cdn.jsdelivr.net/combine/npm/tone@14.7.58,npm/@magenta/music@1.23.1/es6/core.js,npm/html-midi-player@1.5.0"></script>
</head>
<body>
<header>
<h1>Clausic — Rock · Pop · Ambient</h1>
<p class="subtitle">6 original compositions across 3 modern genres</p>
</header>
<nav class="tabs" role="tablist">
<button class="tab active" data-track="new-0">Neon City Riff</button>
<button class="tab" data-track="new-1">Desert Storm</button>
<button class="tab" data-track="new-2">Summer Signal</button>
<button class="tab" data-track="new-3">Midnight Drive</button>
<button class="tab" data-track="new-4">Glass Forest</button>
<button class="tab" data-track="new-5">Ocean Drift</button>
</nav>
<section class="piece active" id="piece-new-0">
<h2>Neon City Riff</h2>
<p class="meta">Rock · E blues · 132 BPM · distorted guitar + power bass</p>
<details class="gen-details">
<summary>View generation code</summary>
<div class="gen-code"><pre><span class="comment">// Neon City Riff — Rock · E blues · 132 BPM</span>
{
<span class="key">root</span>: <span class="str">"E"</span>, <span class="key">scale</span>: <span class="str">"blues"</span> <span class="val">[0,3,5,6,7,10]</span>, <span class="key">bpm</span>: <span class="val">132</span>, <span class="key">bars</span>: <span class="val">16</span>,
<span class="comment">// E blues power chords: E5, G5, A5, Bb5 (i, bIII, IV, bV)</span>
<span class="key">chords</span>: [[0,4,6], [0,4,6], [2,4,8], [1,3,7]],
<span class="comment">// Bluesy melody: mix of blues licks with rests</span>
<span class="key">melody</span>: [4,5,6,null, 4,3,2,null, 0,2,3,4, null,null,5,4, ...],
<span class="key">drums</span>: {
<span class="comment">// Driving rock drums</span>
<span class="key">kick</span>: [1,0,0,0,1,0,1,0], <span class="key">snare</span>: [0,0,1,0,0,0,1,0], <span class="key">hat</span>: [1,1,1,1,1,1,1,1]
},
<span class="key">programs</span>: { <span class="key">melody</span>: <span class="val">30</span> <span class="comment">/* DistortionGuitar */</span>, <span class="key">bass</span>: <span class="val">34</span> <span class="comment">/* ElectricBassPick */</span>, <span class="key">pad</span>: <span class="val">29</span> <span class="comment">/* OverdrivenGuitar */</span> }
}</pre></div>
</details>
<midi-visualizer type="piano-roll" id="viz-0"></midi-visualizer>
<midi-player sound-font visualizer="#viz-0"></midi-player>
</section>
<section class="piece" id="piece-new-1">
<h2>Desert Storm</h2>
<p class="meta">Rock · D minor · 92 BPM · tremolo guitar + driving drums</p>
<details class="gen-details">
<summary>View generation code</summary>
<div class="gen-code"><pre><span class="comment">// Desert Storm — Rock · D minor · 92 BPM</span>
{
<span class="key">root</span>: <span class="str">"D"</span>, <span class="key">scale</span>: <span class="str">"minor"</span>, <span class="key">bpm</span>: <span class="val">92</span>, <span class="key">bars</span>: <span class="val">16</span>,
<span class="comment">// Dm, Bb, C, Am (i, bVI, bVII, v)</span>
<span class="key">chords</span>: [[0,2,4], [5,7,9], [6,8,10], [4,6,8]],
<span class="comment">// Moody, angular minor melody with tremolo feel</span>
<span class="key">melody</span>: [7,null,6,5, 4,null,3,2, 0,null,null,2, 4,5,null,4, ...],
<span class="key">drums</span>: {
<span class="comment">// Heavy, half-time feel drums</span>
<span class="key">kick</span>: [1,0,0,0,0,0,1,0], <span class="key">snare</span>: [0,0,0,0,1,0,0,0], <span class="key">hat</span>: [1,0,1,0,1,0,1,0], <span class="key">ride</span>: [0,1,0,1,0,1,0,1]
},
<span class="key">programs</span>: { <span class="key">melody</span>: <span class="val">29</span> <span class="comment">/* OverdrivenGuitar */</span>, <span class="key">bass</span>: <span class="val">33</span> <span class="comment">/* ElectricBass */</span>, <span class="key">pad</span>: <span class="val">48</span> <span class="comment">/* StringEnsemble */</span> }
}</pre></div>
</details>
<midi-visualizer type="piano-roll" id="viz-1"></midi-visualizer>
<midi-player sound-font visualizer="#viz-1"></midi-player>
</section>
<section class="piece" id="piece-new-2">
<h2>Summer Signal</h2>
<p class="meta">Pop · C major · 118 BPM · bright synth + claps</p>
<details class="gen-details">
<summary>View generation code</summary>
<div class="gen-code"><pre><span class="comment">// Summer Signal — Pop · C major · 118 BPM</span>
{
<span class="key">root</span>: <span class="str">"C"</span>, <span class="key">scale</span>: <span class="str">"major"</span>, <span class="key">bpm</span>: <span class="val">118</span>, <span class="key">bars</span>: <span class="val">16</span>,
<span class="comment">// C, G, Am, F (I, V, vi, IV) — classic pop</span>
<span class="key">chords</span>: [[0,2,4], [4,6,8], [5,7,9], [3,5,7]],
<span class="comment">// Bright, catchy pop melody</span>
<span class="key">melody</span>: [4,5,7,null, 7,5,4,null, 2,4,5,7, null,9,7,5, ...],
<span class="key">drums</span>: {
<span class="comment">// Upbeat pop drums with clap-style snare</span>
<span class="key">kick</span>: [1,0,0,1,1,0,0,0], <span class="key">snare</span>: [0,0,1,0,0,0,1,0], <span class="key">hat</span>: [1,1,1,1,1,1,1,1], <span class="key">openHat</span>: [0,0,0,0,0,0,0,1]
},
<span class="key">programs</span>: { <span class="key">melody</span>: <span class="val">80</span> <span class="comment">/* SynthLeadSquare */</span>, <span class="key">bass</span>: <span class="val">38</span> <span class="comment">/* SynthBass */</span>, <span class="key">pad</span>: <span class="val">4</span> <span class="comment">/* ElectricPiano */</span> }
}</pre></div>
</details>
<midi-visualizer type="piano-roll" id="viz-2"></midi-visualizer>
<midi-player sound-font visualizer="#viz-2"></midi-player>
</section>
<section class="piece" id="piece-new-3">
<h2>Midnight Drive</h2>
<p class="meta">Pop · A minor · 96 BPM · moody keys + smooth bass</p>
<details class="gen-details">
<summary>View generation code</summary>
<div class="gen-code"><pre><span class="comment">// Midnight Drive — Pop · A minor · 96 BPM</span>
{
<span class="key">root</span>: <span class="str">"A"</span>, <span class="key">scale</span>: <span class="str">"minor"</span>, <span class="key">bpm</span>: <span class="val">96</span>, <span class="key">bars</span>: <span class="val">16</span>,
<span class="comment">// Am, F, C, G (i, bVI, bIII, bVII)</span>
<span class="key">chords</span>: [[0,2,4], [5,7,9], [2,4,6], [6,8,10]],
<span class="comment">// Moody, smooth pop melody with longer phrases</span>
<span class="key">melody</span>: [4,null,5,4, 2,null,null,null, 0,2,4,null, 5,6,5,null, ...],
<span class="key">drums</span>: { <span class="key">kick</span>: [1,0,0,0,0,0,1,0], <span class="key">snare</span>: [0,0,1,0,0,1,0,0], <span class="key">hat</span>: [1,0,1,1,1,0,1,1] },
<span class="key">programs</span>: { <span class="key">melody</span>: <span class="val">4</span> <span class="comment">/* ElectricPiano */</span>, <span class="key">bass</span>: <span class="val">33</span> <span class="comment">/* ElectricBass */</span>, <span class="key">pad</span>: <span class="val">89</span> <span class="comment">/* PadWarm */</span> }
}</pre></div>
</details>
<midi-visualizer type="piano-roll" id="viz-3"></midi-visualizer>
<midi-player sound-font visualizer="#viz-3"></midi-player>
</section>
<section class="piece" id="piece-new-4">
<h2>Glass Forest</h2>
<p class="meta">Ambient · F# pentatonic · 52 BPM · crystalline pads</p>
<details class="gen-details">
<summary>View generation code</summary>
<div class="gen-code"><pre><span class="comment">// Glass Forest — Ambient · F# pentatonic · 52 BPM</span>
{
<span class="key">root</span>: <span class="str">"F#"</span>, <span class="key">scale</span>: <span class="str">"pentatonic"</span> <span class="val">[0,2,4,7,9]</span>, <span class="key">bpm</span>: <span class="val">52</span>, <span class="key">bars</span>: <span class="val">16</span>,
<span class="comment">// Wide open voicings cycling through pentatonic clusters</span>
<span class="key">chords</span>: [[0,2,4], [1,3,5], [2,4,6], [0,3,5]],
<span class="key">melody</span>: <span class="val">null</span>,
<span class="comment">// Slow crystalline arpeggios (no standard melody)</span>
<span class="key">arpeggios</span>: [0,null,1,null, 2,null,3,null, 2,null,1,null, 0,null,null,null, ...],
<span class="key">drums</span>: <span class="val">null</span> <span class="comment">// No drums for ambient</span>,
<span class="key">programs</span>: { <span class="key">melody</span>: <span class="val">11</span> <span class="comment">/* Vibraphone */</span>, <span class="key">bass</span>: <span class="val">88</span> <span class="comment">/* PadNewAge */</span>, <span class="key">pad</span>: <span class="val">89</span> <span class="comment">/* PadWarm */</span> }
}</pre></div>
</details>
<midi-visualizer type="piano-roll" id="viz-4"></midi-visualizer>
<midi-player sound-font visualizer="#viz-4"></midi-player>
</section>
<section class="piece" id="piece-new-5">
<h2>Ocean Drift</h2>
<p class="meta">Ambient · C Lydian · 44 BPM · oceanic textures</p>
<details class="gen-details">
<summary>View generation code</summary>
<div class="gen-code"><pre><span class="comment">// Ocean Drift — Ambient · C Lydian · 44 BPM</span>
{
<span class="key">root</span>: <span class="str">"C"</span>, <span class="key">scale</span>: <span class="str">"lydian"</span> <span class="val">[0,2,4,6,7,9,11]</span>, <span class="key">bpm</span>: <span class="val">44</span>, <span class="key">bars</span>: <span class="val">16</span>,
<span class="comment">// Lydian chord voicings: Cmaj7, D/C, Em, F#dim/C</span>
<span class="key">chords</span>: [[0,2,4,6], [1,3,5,7], [2,4,6], [0,3,6]],
<span class="key">melody</span>: <span class="val">null</span>,
<span class="comment">// Slow oceanic arpeggios</span>
<span class="key">arpeggios</span>: [0,null,null,1, null,null,2,null, null,3,null,null, 2,null,null,1, ...],
<span class="key">drums</span>: <span class="val">null</span> <span class="comment">// No drums for ambient</span>,
<span class="key">programs</span>: { <span class="key">melody</span>: <span class="val">88</span> <span class="comment">/* PadNewAge */</span>, <span class="key">bass</span>: <span class="val">48</span> <span class="comment">/* StringEnsemble */</span>, <span class="key">pad</span>: <span class="val">89</span> <span class="comment">/* PadWarm */</span> }
}</pre></div>
</details>
<midi-visualizer type="piano-roll" id="viz-5"></midi-visualizer>
<midi-player sound-font visualizer="#viz-5"></midi-player>
</section>
<script src="script.js"></script>
</body>
</html>Clausic — Rock · Pop · Ambient
6 original MIDI compositions spanning Rock, Pop, and Ambient genres — from high-energy riffs to oceanic ambient textures.