Skip to content

Testing music without speakers ​

A song as data ​

notes(song, fromStep, toStep) lists every note that starts in a window of steps. It's pure JavaScript, so it runs in Node, in a test runner, or in an agent's sandbox, with no audio at all.

ts
import { notes } from "plonkjs";

notes(theme, 0, 16);
// [
//   { time: 0,     instrument: "lead", freq: 523.25, duration: 0.134, velocity: 0.8 },
//   { time: 0,     instrument: "kick", freq: 0,      duration: 0.134, velocity: 0.7 },
//   { time: 0.268, instrument: "lead", freq: 659.26, duration: 0.134, velocity: 0.8 },
//   ...
// ]

Times are in seconds from the start. Windows past the end of the song keep counting, looping through order.

ts
import { expect, test } from "vitest";

test("the chorus brings the drums in", () => {
  const chorus = notes(song, 64, 128);
  expect(chorus.some((n) => n.instrument === "kick")).toBe(true);
});

Other helpers: parse(pattern) for a single pattern, frequency("C#4") for a note's pitch, songSteps(song) and sectionSteps(section) for lengths, stepSeconds(bpm) for timing.

What actually played ​

A player records the last 256 notes it scheduled in player.played, so a game test can check the right music is on:

ts
expect(player.played.some((n) => n.instrument === "kick")).toBe(false); // the map has no drums

How it sounds, in numbers ​

mix(song) renders a song offline in the browser and reports its loudness, balance and problems, so a test or an agent can catch an inaudible track or a clipping mix. See Sound design.

For coding agents ​

Songs are plain text in plain objects, so an agent can write one, change a pattern or an instrument, and check the result with notes() before a person listens. The plonk agent skill gives agents the notation and instrument recipes.