Loom comes out of a long, ongoing exploration into algorithmic
composition and sound installations. I have "performed" three
installations so far, each one based on an 8-speaker autonomous
configuration. Every one of them required new tools — from Pure Data
patches to Ableton Live sets and eventually some as standalone
software. Loom is the culmination of that work. A generative
sample-and-synth application where a folder of audio and a set of
built-in synthesis engines are scheduled as events across up to 8
independent tracks. Each track runs on its own clock, uses its own
pitch and scale, its own engine and its own matrix of algorithm-driven
parameters — including, at the deepest layer, parameters shaped by the
live local weather, HTTP sources, and astronomical events.
Every parameter in the matrix is a row. Each row can be driven
by an internal algorithm — Markov chains, Lorenz attractors, cellular
automata, 1/f noise, and more — or by an external source: live weather,
the sun and moon, planetary positions, microphone input, a webcam, or
any JSON endpoint you point it at. The world outside the app becomes
compositional material.
Loom runs entirely on your machine. No data leaves it except the
optional weather lookup and HTTP sources you configure yourself.
2 · Getting audio in
📁 folder — pick a directory of samples. Or drag a folder onto the drop zone.
▶ next to a file auditions it raw, with no processing.
Clicking a file name selects it for the render preview and analyzes it if needed.
analyze all — decodes each file and extracts key, tempo, loudness, and harmonic content. The first analyzed file's key becomes the reference for auto root note.
Supported formats: WAV, AIFF, MP3, FLAC, OGG. Up to 25 MB each, 50 MB total.
If a file fails to decode it is marked ✗ and skipped.
Loading audio is optional. All five synthesis engines work without it,
and every external-source algorithm works without it. Loom can be a
pure synthesizer if you prefer.
3 · Global controls
length — total seconds of an offline render. Clamped automatically by device memory.
bpm — drives the musical grid. Used by subdivision, quantize, phrase, and meter.
tracks — how many tracks are active (1–8).
fade density — how long the minimum release is. Higher = pads do not clip off.
bit depth / LUFS / normalize — export settings for the master WAV.
scale / root — the global scale. Each track can inherit or override.
global reverb / size — a shared reverb bus every track can send into. Keep size ≤ 2.5 s while streaming.
stream chunk — how many seconds each streaming chunk renders. Default 5 s.
audio output — which physical device Loom plays through. Requires a one-time microphone permission prompt so the browser will reveal device names.
The randomize intensity slider lives in the Global randomizer
panel (§9). It scales how much changes when the randomizer fires.
4 · Track clocks
Each track has its own scheduler. Onset times are not shared
across tracks — the tracks drift in and out of phase with each other.
This is the heart of Loom's texture. The interval between events on a
track is:
interval = eventTiming ÷ (density mod × track density × conductor × automation × source-density)
eventTiming — seconds between events (matrix row). Range 0.05–20 s.
track density — a steady multiplier for this track only.
extreme low mode — a checkbox next to the slider. When on, the slider maps 1 event/minute at its left end to 1 event/hour at its right end. A live countdown shows the time to the next event.
track sensitivity — the per-track attenuator for the global randomizer and dice. Multiplied with the global intensity.
density mod — an algorithmic multiplier on top of track density (matrix row).
release — the tail after the event body. Applies to all engines.
meter — e.g. 4/4, 7/8. Sets beats per bar for grid & phrase.
5 · The matrix
Every parameter is a row. Each row has nine controls.
column
what it does
param
name of the parameter
algorithm
the source of the row's 0…1 value. Internal (Markov, Lorenz, …), weather, sun/moon, planets, mic, vision, HTTP, or manual.
range
min/max the algorithm output maps into. In raw parameter units (Hz for cutoff, semitones for pitch).
p
probability that the row re-samples on each event. 100 = every event. Below 100 = holds the previous value between updates.
influence
blends another row's value into this one. 0 = no effect, 1 = full replacement.
🔒
lock. Protects the row from dice and the timed randomizer.
🎲
randomize this row now (respects lock and intensity).
rz
enable the timed randomizer for this row.
every
seconds between randomizations when rz is on.
🎲
fire the timed randomizer for this row now.
Inertia is fixed per parameter. It smooths the value between
events — cutoff moves slowly, pitch moves more quickly. You can see
every row's current value on the live bar beneath its range.
Engine mapping. Each row affects a specific part of the sound:
pitch → root frequency offset
stretch → sample w/ stretch: granular stretch ratio · FM: subtle ratio nudge
grain → sample w/ stretch: grain window length · FM: index nudge · noise: duration or Q
cutoff → lowpass filter (bypassed in sample · raw)
release → envelope tail (bypassed in sample · raw)
presence → mute/solo curve
subdiv / quantize → grid snap
phrase → phrase-length shaping
6 · Engines
sample — slices from the loaded files. Pitch and time-stretch only apply if the track's stretch / pitch checkboxes are on.
sample · raw — plays the loaded file from its first sample, at its natural pitch and speed, all the way to its end. No filter sweep, no envelope tail — just a 5 ms fade at each edge. The matrix rows cutoff, fade, and release are bypassed. gain, pan, and reverb still apply, so the sample is placed in the mix normally. Use this when you want the file itself, unprocessed.
additive — 2 to 32 sine partials. Partials, inharmonicity (B), tilt, and detune are live-driven by the "add. *" matrix rows. Frequencies follow fn = n·f₀·√(1 + B·n²). Output is normalized so the level is stable across partial counts.
FM — carrier + modulator, with an optional feedback path. Ratio, index β, feedback, and both waveforms come from the track panel and matrix. The labeled index is the true modulation index (depth ÷ modulator frequency).
add+FM — a half-and-half blend.
noise / percussion — white, pink, brown, blue, violet, filtered variants, and six percussion voices. "random" picks per event.
Pitch is always scale-quantized. Each track has an independent pitch
offset so tracks do not all land on the same note.
7 · Meter, subdivision, phrase, presence
subdiv — divisions per beat. With quantize, events snap toward this grid.
phrase — phrase length in beats. Presence is shaped over each phrase.
quantize — 0 = free-running, 1 = fully snapped to the subdiv grid.
8 · Mixer
Each track has a mute button, a solo button, a baseline pan, and an
activity dot. Mute and solo take effect on the next scheduler chunk —
events already playing finish their tail.
The baseline pan is blended with the matrix's pan row:
60% algorithmic, 40% baseline.
9 · Global randomizer
Fires periodically and reseeds the piece. Two modes:
Timer — fires every N minutes (1 – 1440).
Trigger — polls weather and fires when any configured rule edge crosses from false to true.
The intensity slider sets how much of each parameter changes:
0% = frozen, 100% = full re-roll. It is multiplied with each track's
own sensitivity, so both attenuate together.
Rules — one row per metric. Each has two edges. A rule fires
when its reading crosses an edge. Defaults: temperature ≥ 29 °C or
≤ 10 °C; cloud ≥ 80 % or ≤ 10 %; humidity ≥ 85 % or ≤ 25 %;
wind ≥ 8 or ≤ 1 km/h.
A rule's metric can also be an HTTP source. Add an HTTP source in
§11, then open the rule's metric dropdown — the source appears under
http sources. Its reading is checked against your thresholds
the same way weather is.
10 · Source density
Uses any registered source — weather, sun, moon, mic, vision, or an
HTTP value — to continuously scale every track's event rate.
Unlike the trigger rules, which fire discrete events, this applies a
smooth multiplier on every event. The multiplier drifts toward its
target at the smoothing rate, so a fast-moving source becomes
a slow, breathing change in density.
source — which registered source drives the density.
more → less density — when checked, higher readings mean fewer events. When unchecked, higher readings mean more events.
min / max density — the multiplier at the two ends of the source's range. Defaults 0.35× to 1.35×.
smoothing — 0 = snap instantly, 0.85 = very smooth, several minutes of transition.
11 · External sources
Six kinds of external input. Each produces a normalized 0…1 value
that can drive any matrix row, any density driver, or any trigger
rule.
Weather.
Location and poll interval are set in the Global randomizer panel, trigger mode.
Matrix rows: temp, wind, humidity, cloud, pressure, wind dir, rain — plus inverted variants and Δ (change) variants for each.
Wind direction is treated as circular: Δ wind dir uses the shortest angular path, so a shift from 350° to 10° reads as +20°, not −340°.
Astronomy.
Sun elevation and moon illumination. Computed locally from your location. No network calls.
Matrix rows: src · sun elevation, src · moon phase.
Planets.
Ecliptic longitude of Mercury through Neptune, from the astronomy-engine library.
Matrix rows: src · mercury … src · neptune.
These move very slowly. Jupiter cycles in ~12 years. Best used on long-release pads or on a very wide pitch range.
Microphone.
Analyzes room sound. Nothing is recorded — only the current level is read.
Readings: overall RMS, low band (below 250 Hz), high band (2–8 kHz).
Matrix rows: src · mic level, src · mic low band, src · mic high band.
Vision.
Detects people in front of the webcam using COCO-SSD. Nothing is recorded or sent anywhere. The model runs locally.
Readings: person presence (0 or 1), confidence (0–1), activity (bounding-box area, 0–1).
Matrix rows: src · person presence, src · person confidence, src · person activity.
HTTP sources.
Poll any JSON endpoint and extract a number from it.
Set a label, a url, a json path (dot-separated: features.0.properties.mag), a min / max for normalization, and a poll interval.
Example: https://earthquake.usgs.gov/earthquakes/feed/v1.0/summary/all_hour.geojson with path features.0.properties.mag, min 0, max 10. This gives the magnitude of the most recent earthquake worldwide, updating once a minute.
Registered sources appear in the matrix dropdown under sources, and in the Source density panel.
12 · Change detectors — sound that follows movement
Every registered source has two families of algorithms in the matrix
dropdown. The first family reports the source's current value.
The second reports its rate of change.
Value.
src · name — the source's current value, mapped into
the row's range.
src · name ↓ — the same, inverted. High reading gives
low output.
Change. Prefix Δ (the Greek delta, meaning
"change in"):
Δ · name — any change, up or down. The row's output
is proportional to how fast the source is moving.
Δ · name ↑ — rising change only. Output spikes when
the value increases; falling change gives zero.
Δ · name ↓ — falling change only. Output spikes when
the value decreases; rising change gives zero.
Δ · name ⚡ — one spike per threshold crossing. Fires
a single pulse the moment the value crosses a small band, then
waits for the reverse crossing before firing again.
A change detector reads its source every time it is sampled (see
the source's poll interval in the relevant panel). It compares the
new reading to the previous one and outputs a value proportional to
the difference. That output then decays back to zero over roughly
six seconds of no movement. Steady signal, silent output. Movement,
output.
Behaviour in the matrix.
A Δ · row on gain makes the track sound only
when its source moves — a clap, a gust, an earthquake. Between
events, the track is silent.
A Δ · row on cutoff opens the filter briefly
on each change, then lets it settle. The ear hears a sweep.
A Δ · row on presence gates the whole track
by movement — nothing arrives, no sound.
Combining a Δ · row with a high release on the
track (2–4 s) lengthens the swell. High release plus a
long-release envelope turns every spike into a slow bloom.
Preconditions.
Weather sources need the Global randomizer in trigger mode
with a resolved location. Without that, weather readings are
absent and the change detector stays at zero.
Astronomy needs the Astronomy panel enabled and a location
resolved.
Mic needs the Microphone panel enabled with a device
selected.
Vision needs the Vision panel enabled and a person in
frame.
HTTP sources need to be registered and enabled.
If a precondition isn't met, the change detector sits at zero and
the row is silent. That's not an error — the source simply has no
movement to report.
Change-gate presets. Three presets in the track's preset
dropdown install a change-gate on the current track in one click:
change gate · weather — additive cloud with a temperature
gate. Gain is bound to Δ weather · temp, range
[0, 1]. The track sounds when the temperature moves
and decays over a few seconds.
change gate · mic — FM voice whose cutoff and FM index
follow the mic's high band and whose pitch follows the low band.
Gain is bound to Δ · mic level. The track sounds on
room activity and stays silent otherwise.
change gate · earthquake — slow inharmonic additive with a
six-second release. Gain is bound to Δ · earthquake,
referring to the first HTTP source by its id
(src-http-1-delta). The id is fixed at
http-1 regardless of the label. If your earthquake
source has a different id, edit the preset or renumber your
sources so the earthquake is the first one.
Each preset sets the track's gain row to the relevant
Δ · algorithm with range [0, 1], forces
presence to manual at [0.9, 1] so gain is the
deciding factor, sets eventTiming to roughly one event every
one to three seconds, and lengthens the track's release (2.5–6 s) so
the change spike decays audibly.
13 · Weather history & replay
Every weather poll is stored. The Weather history · replay
panel turns that log into a form.
buffer — how many readings have been stored. Grows by one per poll, plus one per location resolve.
coverage — the time span those readings span.
window — 1 hour to 30 days. The slice of the buffer a single sweep covers.
speed — 1× is real time. 60× compresses 1 hour of history into 1 minute. 3600× compresses a day into 24 seconds. Up to 10000×.
scrub bar — drag to move the cursor through history by hand.
jump to now — snap the cursor to the newest sample.
While replay is active, live polling keeps running in the background
so the buffer keeps growing — but the replayed values own the
matrix. Turn replay off to return to live weather.
This is Loom's compressed-time-scale mechanism. A 24-hour weather
cycle becomes a 24-second musical gesture. Two tracks can be bound to
the same window at different speeds, giving you two simultaneous time
scales of the same underlying signal.
14 · Automation
A one-hour intensity curve that scales every track's event rate over
time. Horizontal axis = 0 → 60 minutes, one point every 5 seconds.
Vertical axis = intensity 0…1.
0 at a point → that moment plays at 0.25× the conductor's rate.
1 at a point → that moment plays at 1.75× the conductor's rate.
Draw with the mouse. Shift-drag for a straight line between two points. Double-click flattens ±1 minute around the click back to 1.0.
flat / ramp ↑ / ramp ↓ / random — quick fills.
enabled — the curve is bypassed (rate × 1.0) when unchecked.
loop — when on, the curve wraps every hour.
re-roll — when on (and loop is on), a fresh random curve is generated at every loop wrap.
15 · Conductor
A mode × track grid. Each row is a mode — an arrangement of
per-track density multipliers. Each column is a track.
Values range 0 to 1. A cell's fill height shows the value. Each value multiplies that track's event rate while the mode is active (clamped to 0.35× – 1.6×).
Drag a cell up or down to adjust. Click a cell to type an exact value. Shift-click to select its row without editing.
Click a row's name to select that mode, or rename it.
+ mode adds a new row. 🎲 randomize reseeds every cell. ❄ hold freezes the conductor on the selected mode for 30 seconds.
The row with the solid outline is playing. The row with the dashed outline is selected — it becomes the starting point next time you press ∞ stream.
16 · Visual field
The canvas below the matrix is a continuous particle field. Each sonic
event becomes a particle colored by track. Direction turns every 30
chunks; density biases angle and energy. Double-click the
canvas for fullscreen; Esc to exit.
Effects layered on the field:
Constellation memory — small dots drift away from recent events, half with the flow direction, half against it. They fade over about 90 seconds and bounce off the canvas edges.
Automation vignette — the corners darken when the automation curve is low and clear when it is high. The composition lights its own canvas.
Conductor tint — a very faint hue on the whole field that crossfades every three minutes as the conductor mode changes. Golden-angle spacing keeps neighbouring modes from looking alike.
Line mode — per track, choose horizontal, vertical, both, or off. Horizontal draws trails in event order. Vertical buckets particles into time columns and connects them from low to high pitch.
17 · Panel focus modes
The ⇔ panels button in the header — or the p key —
cycles through four layouts:
full — standard layout. Every panel remembers its state.
viz only — the visual field fills the middle. Side panels are collapsed to their title bars on the left and right.
track only — the Track panel fills the middle. Its MIDI and Matrix sections are collapsed. Side panels are collapsed to title bars.
all open — every side panel and every Track section expanded.
Your last stage is remembered across reloads. Returning to full
restores the exact panel and section state you had before entering
any focus stage.
18 · Render vs. stream
∞ stream — endless. Renders one chunk ahead of the playhead.
⬤ render — full offline export. Produces a master WAV and per-track stems.
↓ tap — download the streaming tap (the last ~3 minutes of whatever has played).
↓ save recipe — export the current setup as JSON.
↑ load recipe — import a recipe JSON and apply it.
19 · Recipes & seed
The seed in the header makes everything reproducible: same
seed + same recipe = same output. The recipe JSON includes the seed,
all track settings, all routing, the conductor, the automation curve,
the source density config, the global randomizer config, the mixer
state, and the export settings.
20 · The algorithm library
Every algorithm in the matrix dropdown returns a number between 0
and 1. The row's range maps that into real units.
manual — a fixed value at the midpoint of the range.
random — picks a different algorithm every 8 calls.
Markov — first-order Markov chain over 4 states.
Lorenz / Rössler / Clifford / Thomas — chaotic attractors.
Loom can drive other software — a DAW, a modular rig, a
visualiser, a Max patch — using everything described above as a
control signal. Two mechanisms exist.
A. Direct per-track MIDI out.
Enable MIDI out in the MIDI panel and pick a port.
Per track: send MIDI, channel, and CC numbers for cutoff, pan, presence, and mod.
Every event that fires on a track emits a note-on, a note-off, and up to four CC messages on the chosen channel.
The note number is the track's actual pitch. The CC values are derived from the event's parameters, one CC per parameter. In another app, map those CCs to whatever you want — the note number gives you pitch, the CCs give you the row values at the moment of the event.
B. Matrix → CC map.
In the track's MIDI section, use + add mapping to assign any matrix row to any CC number.
On every event, Loom normalizes the row's current value into 0–127 and sends it as that CC.
This means you can route any external source to any CC in another app. Examples below.
Worked examples.
1. Weather cloud cover → a reverb wet/dry in Ableton.
Set the cutoff row's algorithm to weather · cloud, range [0, 1].
In the track's MIDI section, add a mapping: cutoff → CC 74.
Enable send MIDI, and select your virtual MIDI port.
In Ableton, MIDI-map CC 74 to the wet/dry knob of a reverb. As cloud cover rises, the reverb opens.
2. Mic level → a filter cutoff in a hardware synth.
Set the cutoff row to src · mic level, range [200, 8000].
Add a mapping: cutoff → CC 74.
Send MIDI to the synth. As the room fills with sound, the filter opens. The synth breathes with the room.
3. Webcam presence → gate a track in a DAW.
Set the presence row to src · person presence, range [0, 1].
Add a mapping: presence → CC 11.
Map CC 11 in the DAW to a gate or a track's volume. A person walking into view unmutes the track.
4. Earthquake magnitude → a filter in VCV Rack.
Add the USGS HTTP source.
Set the cutoff row to src · earthquake, range [100, 12000].
Add a mapping: cutoff → CC 74.
Send MIDI to VCV. A big quake opens the filter wide; quiet periods close it.
5. Sun elevation → a slow pan in Max/MSP.
Set the pan row to src · sun elevation, range [-1, 1].
Map pan → CC 10.
The piece drifts left-to-right over the course of a day.
6. Conductor-driven rhythm → an external drum machine.
On a percussion track, enable send MIDI. Its note-ons fire in sync with the internal scheduler — every event is a note.
Send MIDI to the drum machine. The drum machine triggers on note-on. The conductor, the automation curve, and source-density all modulate the timing without the drum machine needing to know anything about them.
What Loom cannot send.
Audio — only the render / stream paths produce audio.
Continuous high-resolution data — MIDI CC updates only at event boundaries (once per scheduled event). For smooth continuous streams at audio rate, use the render export or an audio route.
Values above 127 or below 0 — everything normalizes to MIDI's 7-bit range. If you need more resolution, split one source across two CCs (high byte / low byte) and recombine in the receiving app.
22 · MIDI in — control Loom with a controller
Loom can be driven from a MIDI controller. Any knob, fader,
button, or pad that emits a MIDI CC message can control a slider,
a matrix range, or a dice action. Everything is learnable — no
manual CC mapping tables.
Enabling it.
Open the MIDI in panel in the right column.
Tick enable. The browser will ask for MIDI permission the first time.
Pick your controller from the input dropdown. If you plug it in later, click ↻ ports.
The last CC readout shows incoming messages — useful to confirm the controller is being seen.
Learning a binding.
Click ⌁ learn. Every bindable slider gets a dashed accent outline.
Click the slider you want to control. It gets a solid orange outline.
Move the knob on your controller. The binding is created and appears in the list below.
The slider's thumb turns magenta to indicate it's MIDI-bound.
Right-click a bound slider to remove its binding. clear all removes every binding.
Channels.
Bindings are channel-aware. If your controller sends different
tracks on different MIDI channels, the bindings coexist:
Track 1's FM index bound to CC 74 on channel 1.
Track 2's FM index bound to CC 74 on channel 2.
Turn either knob and only its track responds.
If your controller only sends on one channel, use different CC
numbers instead — CC 74 for track 1, CC 75 for track 2, etc.
Two kinds of targets.
Value targets — a slider or a matrix range input. The CC value 0–127 maps linearly into the target's range.
Trigger targets — a button like 🎲 global or Δ gate. The CC fires the action once when it rises above 64, then re-arms when it drops below. Holding a knob high doesn't repeat.
Override vs. nudge.
Two ways to bind a CC to an algorithmic parameter, depending on
whether you want the algorithm to keep running:
Override — bind to a Track panel slider (e.g. FM index). The row's algorithm is set to manual, the CC becomes the value, and the algorithm stops advancing.
Nudge — bind to a matrix range input (min or max). The algorithm keeps running; the CC widens or narrows the window its output maps into.
An example: bind a knob to FM index in the Track panel and
the knob takes over completely. Bind it to fmIndex max in
the matrix and the Markov (or Lorenz, or Life) algorithm keeps
driving the index, but now within the range your knob sets.
Restoring algorithmic control.
Every time a CC takes over a track row, the row's prior state is
snapshotted. Two ways to hand control back:
↺ restore now — restores every overridden row on demand, without disabling MIDI in.
restore on disable — when this is checked, unchecking the enable box restores all overridden rows automatically.
Both actions are undoable with Cmd+Z. The N overrides
counter next to the checkbox shows how many rows are currently
being held by a CC.
What can be controlled.
Global — master volume, global reverb amount, reverb size, randomize intensity, conductor switch probability, stream chunk length, source density (min, max, smoothing).
Per track — FM ratio, FM index, FM feedback, additive partials, inharmonicity, tilt, detune, track density, release, sensitivity.
Matrix — every row's min and max range input (22 rows × 2 = 44 targets per track).
Mixer — pan and volume for every track.
Triggers — 🎲 global, 🎲 track, 🎲 all tracks, Δ gate. Per-track dice targets exist too but aren't exposed as buttons — bind them by hand in the recipe JSON with IDs like action:trackDice:0.
Recipes and persistence.
Bindings are stored in localStorage so they survive a
reload, and they're also saved inside recipes. A recipe carries
every binding — CC number, channel, and target. Loading a recipe
restores them; if the controller on the new machine has a
different device ID, the port must be re-picked but the CC and
channel bindings remain valid.
Web MIDI support.
Web MIDI is supported in Chrome, Edge, and Opera — including the
Electron build. Firefox and Safari require a flag or don't
support it at all. If Web MIDI: not available appears in
the panel, switch to Chrome or use the packaged app.
23 · Tips
Evolving density — put algorithms on eventTiming and density mod.
Subtle evolution — set randomize intensity to 10–30 %.
Sparse textures — turn on extreme low mode and push the slider right.
Long-form shaping — draw the Automation curve.
Endless variation — Automation loop + re-roll.
Live weather reactions — Global randomizer in trigger mode with a resolved city, then a track preset from weather-driven.
Replay a day of weather in 30 seconds — Weather history · replay, window 24 h, speed 2880×.
Two timescales at once — bind two tracks to the same weather metric. Set one track's source density to the weather panel, and the other to the replay panel. Different windows, different speeds.
If the tab gets heavy, lower stream chunk, reduce tracks, or reduce global reverb size.
Serve over http://localhost or https:// — AudioWorklet will not load from file://.
Available also as OSX application - no need for Internet connection, except for gathering weather, http, and astronomical data as sources.
24 · Semantic tagging & AI labeling
Semantic tagging runs a small neural audio classifier (YAMNet,
~4 MB) over every analyzed file and stores the top matching
AudioSet categories. There are 521 possible labels — things like
Speech, Music, Percussion,
Drum, Guitar, Rain,
Explosion, Bird, Vehicle.
Once tagged, files can be filtered and weighted by what they
are, not just by their pitch, loudness, or length.
The model runs entirely on your machine. No audio is sent
anywhere, no network call is made during tagging. Inference takes
roughly 200–400 ms per file on a modern laptop, and it happens
once — in the background, while you keep using the app.
Enabling it.
Open the Sources panel and tick semantic tags.
The status next to it goes loading… → ready · N tags.
Files already analyzed before the model loaded are re-tagged
automatically the moment the model becomes available.
Each file's entry in the list gains a suffix like
[Percussion, Drum, Music].
If you later load more files, they're tagged as part of their
normal analysis pass.
The four tag-based source modes.
When semantic tags are available, the Track panel's source
mode dropdown gains four new options. Each one uses the tag
data in a different way:
only [tag] — restrict the pool to files carrying that
tag with confidence ≥ 0.15. If nothing matches, it falls back
to the first analyzed file.
prefer [tag] — files that carry the tag get up to 4×
the weight of files that don't. Non-matching files still play
occasionally, so the texture stays varied.
avoid [tag] — files that carry the tag get their
weight cut to 15 %. Useful for filtering out a specific
category (voices, sirens, percussive hits) from an otherwise
busy folder.
search (fuzzy) — a free-text search field instead of a
dropdown. Type drum, metal,
speech, or a multi-word phrase like
metallic percussion. Files whose tags match the
query score higher; substring and word-overlap matches are
supported. A live readout shows how many files match.
How search matching works.
Each file is scored against the query using four levels of match:
an exact tag name (Drum vs. drum)
scores highest; a substring match (Drum kit contains
drum) scores 85 %; a reverse substring
(drum appears inside a longer query) scores 75 %;
and word-level overlap across multi-word queries scores 40–80 %.
The final weight is the tag's own confidence multiplied by the
match level. Files that score zero still get a 5 % floor weight,
so a query with no exact hits never goes silent.
In recipes.
The chosen tag or search string is stored in the recipe as
sourcePool.tagFilter, so saving and loading preserves
which filter was active. The tags themselves are recomputed
locally — they aren't serialized, since they depend on the
specific files loaded on the machine that opens the recipe.
In the Electron build.
The YAMNet model and the 521 AudioSet class names are bundled
with the app. Nothing is fetched from the network during
tagging. The whole feature works offline.
Privacy — Loom runs locally. Nothing is uploaded. The only
network calls are the optional weather lookup, geocoding, and any
HTTP source you configure.
welcome shows the play-instantly choice; tour jumps to the deep walkthrough
Global randomizer
timer
force a fire now — no waiting
minutes
next fire in
minutes
next poll in
rules
no rules yet — add one below to let weather trigger the randomizer.
each metric has two edges. the randomizer fires whenever any edge crosses from false to true.
idle
Source density
idle
scales every track's event rate using any registered source — weather,
sun, moon, mic level, or a custom HTTP value.
raw—
target—
applied1.00×
Astronomy
idle
sun elevation and moon phase, computed locally from the Global randomizer's
location. No network calls.
sun elevation—
sun state—
moon illumination—
moon state—
add the entries src · sun elevation or src · moon phase
to any matrix row, or select them in the Source density panel above.
Weather history · replay
off
records every weather poll, then replays the buffer at a compressed
time scale. bind matrix rows to weather · * to hear it.
1 hour per minute
oldestnow
buffer—
coverage—
position—
applied—
no history yet. enable the Global randomizer in trigger mode with a
resolved location, and let it poll for a while.
Microphone
off
analyzes room sound. Use as a matrix source, a density driver,
or a trigger. Nothing is recorded — only the current level is read.
rms—
low band—
high band—
status—
add src · mic level, src · mic low band, or
src · mic high band to any matrix row.
Vision
off
detects person presence via webcam. nothing is recorded or sent.
persons—
confidence—
activity—
HTTP sources
poll any JSON endpoint. each value becomes a source you can use in the
matrix or the Source density panel.
direction turns every 30 chunks · density biases angle & energy · continuous
Samples
off
or drop folder here
No sources loaded.
click ▶ on a file to audition it raw (no processing). click the name to select it for the render preview.
sources are used for analysis (key, tempo, loudness) even when synthesis engines are active.
Global
s
describe a preset in a sentence — engines, count, density, scale, algorithm