quasimusic studio
Manual
Back to studio

User manual

quasimusic studio turns a symbolic substitution into self-similar music.

You define a substitution rule on a finite alphabet, the app computes the Perron-Frobenius geometry of that rule, lays out the corresponding one-dimensional substitution tiling in several layers, and plays each tile as a note. You can shape the sound, export a MIDI file, and share a setup by link.

It is one self-contained HTML file. It needs no installation, no account, and no internet connection once loaded. Open it by double-clicking, or visit the hosted version. The geometry it produces is identical to the quasimusic Python package, so a setup here and the same setup in the package generate the same tilings and the same note timings. The underlying ideas are from R. Trevino, "Quasimusic: tilings and metre", Journal of Mathematics and the Arts, 2022.

The big idea Quick start System Substitution rule Reading the output Substitution matrix Characteristic polynomial Layer mixer Sound Transport The tiling view Exporting MIDI Sharing a setup Recipes Troubleshooting A little more math

The big idea in one paragraph

A substitution replaces each symbol with a finite word over the same alphabet. Iterating it builds longer and longer words with rigid self-similar structure. The substitution matrix records how many of each symbol appears in each image; its leading (Perron-Frobenius) eigenvalue and eigenvector set a natural length for each symbol's tile so that applying the substitution scales the whole tiling by a single factor. Reading those tiles left to right in time, and assigning a pitch or chord to each symbol, turns the tiling into a rhythm and a melody. Stacking several substitution depths gives layers that share the same total length but subdivide it at different rates, which is where the music gets its nested, quasi-periodic feel.

Quick start

  1. Open the app. It starts on the Fibonacci substitution (0 maps to 0 1, and 1 maps to 0).
  2. Press Play. A playhead sweeps across the layers and you hear the tiling.
  3. Change the rule, the number of layers, or the tempo and press Play again.
  4. When you have something you like, press Download MIDI, or Copy share link to save the setup.

If you hear nothing, click anywhere in the page once and press Play again. Browsers block audio until you interact with the page; pressing Play counts, so this only ever bites on the very first try.

The controls: System

This panel sets the structure of the piece.

A practical note on iterations: the finest layer has as many tiles as the length of SN, which grows quickly. If that exceeds 5000 tiles the app stops drawing and asks you to lower iterations, so the browser stays responsive. Rules with a larger Perron-Frobenius value reach that ceiling at lower iteration counts.

Substitution rule

One row per symbol. Each row shows the symbol's colour and number, and a text box for its image. Type the image as symbols separated by spaces, for example 0 1. Commas work too. The image must be non-empty and use only symbols that exist in the current alphabet. If you type something invalid the box turns red and the app keeps the last valid rule until you fix it.

Reading the output

The readout strip across the top reports the geometry.

Substitution matrix

The matrix M where the entry in row i, column k counts how many times symbol k appears in the image of symbol i. Positive entries are highlighted. This is the object whose spectral data drives everything else.

Characteristic polynomial

The polynomial det(λI − M), shown with exact integer coefficients (for example λ2 − λ − 1 for Fibonacci). The Perron-Frobenius value in the readout is its largest real root.

Layer mixer

This is where you control sound per layer. A Lite / Full synth switch sits at the top.

In both modes, each layer has a channel strip on the left with a volume slider (live; move it while the piece plays and hear the balance change) and a waveform selector (triangle, sine, saw, or square).

In Full synth mode each strip also gets an envelope:

Envelope edits take effect the next time you press Play, because notes are scheduled with their envelopes at the moment playback starts. Volume changes, by contrast, are immediate.

The strip layout adapts to whether you are editing pitches. In Auto mode each strip shows that layer's pitch map, listing which note each symbol plays. In Custom chord mode the editable chord cells appear to the right of each strip, so a row is one layer's volume, timbre, envelope, and chords together. With many symbols the chord area scrolls sideways while the channel strip stays pinned on the left.

Sound

This panel decides where pitches come from.

Master Volume sits at the bottom and scales everything.

Transport

The tiling view

The stacked strips are the heart of the tool. The top strip is the finest layer, SN. Each strip below it is one substitution coarser, and every strip spans the same total width because all layers have the same length. Tiles are coloured by symbol; the legend maps colours to symbols. Hover a tile to read its symbol, its onset in seconds, and its duration. During playback a vertical playhead sweeps across all layers and the tile currently sounding in each layer is highlighted.

A warning bar appears above the matrix when the rule is not primitive, or when it has no positive Perron-Frobenius eigenvector. In those cases the durations may not be well defined or unique, and you should read the tiling with caution. Primitive rules are the well-behaved case and are what the construction assumes.

Exporting MIDI

Download MIDI writes a standard MIDI file built from the current setup:

What does not travel into MIDI: the waveform and the ADSR envelope. MIDI carries notes and a few controllers, not synthesis. In a DAW you would choose an instrument for each track instead, which is the natural equivalent. This is the same setup as the quasimusic Python package, so exports from the app and the package are consistent.

Sharing a setup

Copy share link encodes the whole setup, the rule, every slider, the sound mode, the per-layer mix, the envelopes, and any custom chords, into the page address and copies it to your clipboard. Open that link and the app rebuilds exactly that setup. This is the simplest way to save a piece or send one to someone, and it needs no files. The link can get long if you have hand-edited a large chord grid, which is expected; it still pastes fine.

Recipes

Recreate the Fibonacci tiling

Alphabet 2, rule 0 maps to 0 1 and 1 maps to 0, seed 0. The characteristic polynomial reads λ2 − λ − 1 and the Perron-Frobenius value is the golden ratio.

Try the tribonacci rule

Alphabet 3, with 0 maps to 0 1, 1 maps to 0 2, 2 maps to 0. The Perron-Frobenius value is the tribonacci constant, about 1.8393.

Build your own

Pick an alphabet size, give every symbol an image, and watch the Primitive badge. If it stays red, add cross-references between symbols until every symbol can eventually reach every other; that is what primitivity means in practice.

Make the layers feel different

Switch the mixer to Full synth and give the bass (coarse) layers slow attacks and long releases, and the finest layer a short, percussive envelope. Set different waveforms per layer so the lines separate by timbre as well as register.

Troubleshooting

No sound. Click the page once, then press Play. If still silent, raise Master Volume and the per-layer volumes, and confirm your system output is not muted.

The tiling area says there are too many tiles. Lower Iterations. Rules with a large Perron-Frobenius value reach the tile ceiling quickly.

Playback is declined for too many notes. Lower Layers or Iterations, or use smaller chords in Custom mode.

A red warning bar appears. The rule is not primitive (or has no positive eigenvector). The piece may still play, but the durations are not on firm footing. Adjust the rule so every symbol can reach every other.

A chord cell or rule box is red. The text is not valid. For rules, use only existing symbol numbers separated by spaces. For chords, use MIDI numbers 0 to 127 or note names like C4; an empty cell is a rest.

The chord grid runs off the side. That is the honest shape of a layers-by-symbols grid at large alphabets. Scroll it sideways; the channel strips stay pinned.

A little more math, for the curious

The substitution matrix M is non-negative. When the substitution is primitive, some power of M is strictly positive, and the Perron-Frobenius theorem guarantees a simple largest eigenvalue λ > 0 with a strictly positive eigenvector. The app checks primitivity by taking boolean reachability powers of M up to the Wielandt bound (A − 1)2 + 1, which is exactly how far you must look before concluding a rule is not primitive. It uses boolean powers rather than the literal matrix powers so the test never overflows, even for large alphabets.

The right Perron-Frobenius eigenvector gives each symbol a base tile length, normalised so that the whole construction has a chosen total duration at the chosen tempo. Applying the substitution once multiplies the tiling's scale by λ, which is why each coarser layer is a factor of λ longer per tile yet the layers share a total length. For the full development, see the paper cited at the top.

Back to studio