Create music with JavaScript. Use simple strings and arrays to craft rhythms, melodies, and chord progressions — then export MIDI files or play them live in the browser with Tone.js or use the CLI to directly emit MIDI file from your terminal.
npm install scribbletuneIf you installed Scribbletune globally via npm i -g scribbletune then you can directly use scribbletune as the command. If you installed it locally via npm i scribbletune then please use npx scribbletune as the command.
Run modes:
# Global install
npm install -g scribbletune
scribbletune --help
# Local/project install
npm install scribbletune
npx scribbletune --helpQuick command examples:
scribbletune --riff <root> <mode> <pattern> <subdiv> [options]
scribbletune --chord <root> <mode> <pattern> <subdiv> <progression|random> [options]
scribbletune --arp <root> <mode> <pattern> <subdiv> <progression|random> [options]Progression input rules for --chord and --arp:
1645 # degree digits
"I IV vi V" # roman numerals (space separated)
I,IV,vi,V # roman numerals (comma separated)
random # generated progression
CM-FM-Am-GM # explicit chord names (`root` and `mode` are ignored)Common options:
--outfile <file.mid> # default: music.mid
--subdiv <4n|8n|1m...>
--sizzle [sin|cos|rampUp|rampDown] [reps]
--sizzle-reps <number>
--amp <0-127>
--accent <x--x...>
--accent-low <0-127>
--style <letters> # riff motif/style, e.g. AABC
--fit-pattern # explicit enable (already enabled by default)
--no-fit-pattern # disable automatic pattern fitting
--bpm <number> # your DAW may or may not support itNote: if your pattern uses [ and ] (for subdivisions), quote it in shell:
scribbletune --arp C3 major 1 'x-x[xx]-x-[xx]' 16nPattern helpers:
x.repeat(4) # -> xxxx
'x-x[xx]'.repeat(2)
2(x-x[xx]) # prefix repeat shorthand
(x-x[xx])2 # suffix repeat shorthand# Basic riff from scale
scribbletune --riff C3 phrygian x-xRx_RR --outfile riff.mid
# With motif/style and positional subdiv
scribbletune --riff C3 phrygian x-xRx_RR 8n --style AABC --sizzle sin 2 --outfile riff-aabc.mid
# Set riff subdivision via positional arg
scribbletune --riff C3 phrygian x-xRx_RR 8n --style AABC --outfile riff-8n.mid
# Pattern with subdivisions (quote [] in shell)
scribbletune --riff C3 phrygian 'x-x[xx]-x-[xx]' 8n --style AABC --outfile riff-subdiv.midRiff + motif note:
--stylecreates riff sections by repeating the full pattern per letter.- Example:
--style AABCwith patternx-x[xx]creates 4 sections:A,A,B,C. - Repeated letters reuse the exact same generated section (same rhythm and same notes, including random
Rchoices).
# Degree digits (resolved against root/mode)
scribbletune --chord C3 major xxxx 1m 1645 --sizzle cos 1 --outfile chords-1645.mid
# Roman numerals (space/comma separated)
scribbletune --chord C3 major xxxx 1m "I IV vi V" --outfile chords-roman.mid
# Random progression
scribbletune --chord C3 major xxxx 1m random --outfile chords-random.mid
# Explicit chord names (root/mode currently ignored for this style)
scribbletune --chord C3 major xxxx 1m CM-FM-Am-GM --outfile chords-explicit.mid
# Subdivisions in pattern
scribbletune --chord C3 major 'x-x[xx]-x-[xx]' 8n I,IV,vi,V --outfile chords-subdiv.mid# Arp from degree progression
scribbletune --arp C3 major xxxx 1m 1736 --sizzle cos 4 --outfile arp-1736.mid
# Single degree "1" means tonic chord in the chosen key/mode
scribbletune --arp C3 major xxxx 4n 1 --outfile arp-degree-1.mid
# Arp from explicit chords
scribbletune --arp C3 major xxxx 1m CM-FM-Am-GM --count 4 --order 1234 --outfile arp-explicit.mid
# Custom note order inside each arpeggiated chord (one-based)
scribbletune --arp C3 major xxxx 4n 1 --order 2143 --outfile arp-order-2143.mid
# Same custom order using local dist build
node dist/cli.cjs --arp C3 major xxxx 4n 1 --order 2143 --outfile arp-order-local.mid
# Auto-fit is default (single x expands to full generated arp length)
scribbletune --arp C3 major x 4n 1736 --outfile arp-fit-default.mid
# Disable auto-fit if you want a short clip
scribbletune --arp C3 major x 4n 1736 --no-fit-pattern --outfile arp-no-fit.mid--order behavior:
- One-based order is supported (
1234,2143) and is recommended. - Zero-based order is also accepted for backward compatibility (
0123,1032).
Run scribbletune --help to see the latest CLI usage text.
import { scale, clip, midi } from "scribbletune";
const notes = scale("C4 major");
const c = clip({ notes, pattern: "x".repeat(8) });
midi(c, "c-major.mid");Run it with node and open the .mid file in Ableton Live, GarageBand, Logic, or any DAW.
Scribbletune's browser entry point adds Session, Channel, and live clip() support on top of Tone.js.
import { Session } from "scribbletune/browser";
const session = new Session();
const channel = session.createChannel({
instrument: "PolySynth",
clips: [
{ pattern: "x-x-", notes: "C4 E4 G4" },
{ pattern: "[-xx]", notes: "C4 D#4" },
],
});
await Tone.start();
Tone.Transport.start();
channel.startClip(0);import { clip } from "scribbletune/browser";
const kickClip = clip({
sample: "https://scribbletune.com/sounds/kick.wav",
pattern: "x-x-",
});
const btn = document.querySelector("#btn");
btn.addEventListener("click", async () => {
await Tone.start();
Tone.Transport.start();
kickClip.start(0);
});Scribbletune uses a simple string notation to describe rhythms:
| Char | Meaning |
|---|---|
x |
Note on |
- |
Note off (rest) |
_ |
Sustain previous note |
R |
Random note (from randomNotes pool) |
[] |
Subdivide (e.g. [xx] = two notes in one beat) |
"x---x---x-x-x---"; // basic kick pattern
"[xx][xx]x-x-"; // hihat with subdivisions
"x___"; // one long sustained notePowered by harmonics:
import { scale, chord, scales, chords } from "scribbletune";
scale("C4 major"); // ['C4', 'D4', 'E4', 'F4', 'G4', 'A4', 'B4']
chord("CM"); // ['C4', 'E4', 'G4']
scales(); // list all available scale names
chords(); // list all available chord namesimport { arp } from "scribbletune";
arp({ chords: "CM FM", count: 4, order: "0123" });
// ['C4', 'E4', 'G4', 'C5', 'F4', 'A4', 'C5', 'F5']import { progression, getChordsByProgression } from "scribbletune";
progression("M", 4); // e.g. ['I', 'ii', 'V', 'IV']
getChordsByProgression("C4 major", "I IV V IV");
// 'CM_4 FM_4 GM_4 FM_4'The browser entry point (scribbletune/browser) provides everything above plus:
import { Session } from "scribbletune/browser";
const session = new Session();
const drums = session.createChannel({
sample: "https://scribbletune.com/sounds/kick.wav",
clips: [{ pattern: "x---x---" }, { pattern: "x-x-x-x-" }],
});
const synth = session.createChannel({
instrument: "PolySynth",
clips: [{ pattern: "x-x-", notes: "C4 E4 G4" }],
});
await Tone.start();
Tone.Transport.start();
// Start clips independently
drums.startClip(0);
synth.startClip(0);
// Switch patterns on the fly
drums.startClip(1);
// Or start a row across all channels
session.startRow(0);Channels accept various sound sources:
// Built-in Tone.js synth (by name)
{ instrument: 'PolySynth' }
// Pre-built Tone.js instrument
{ instrument: new Tone.FMSynth() }
// Audio sample URL
{ sample: 'https://example.com/kick.wav' }
// Multi-sample instrument
{ samples: { C3: 'piano-c3.wav', D3: 'piano-d3.wav' } }
// With effects
{ instrument: 'PolySynth', effects: ['Chorus', 'Reverb'] }| Export | Description |
|---|---|
clip(params) |
Create a clip — returns note objects (Node.js) or a Tone.Sequence (browser) |
midi(clip, filename?) |
Export a clip to a MIDI file |
scale(name) |
Get notes of a scale, e.g. 'C4 minor' |
chord(name) |
Get notes of a chord, e.g. 'CM' |
scales() |
List all available scale names |
chords() |
List all available chord names |
arp(params) |
Generate arpeggiated note sequences |
progression(type, count) |
Generate a chord progression ('M' or 'm') |
getChordsByProgression(scale, degrees) |
Convert Roman numeral degrees to chord names |
getChordDegrees(mode) |
Get Roman numeral degrees for a mode |
Session |
(browser only) Manage multiple channels and coordinate playback |
npm install # install dependencies
npm test # run tests
npm run build # build with tsup
npm run lint # check with biome
npm run dev # build in watch modeIf developing new features for the CLI, use the following command after running npm run build to test before publishing,
node dist/cli.cjs --helpMIT
