scripts / norns
grainloom
Granular sampler, looper, tape generation loss, glitch and reverb for monome norns.
install
In maiden's REPL (the >> prompt at the bottom of the editor), run:
;install https://github.com/c4pt0r/grainloomOr add the splines catalog to maiden and install it from the project manager. Restart norns if the script ships a new engine.
readme from c4pt0r/grainloom
grainloom
A minimal continuous feedback loop and slice machine for monome norns.
Grainloom continuously records a mono input into a circular Buffer while playing that Buffer. On every pass, new input is mixed with a decayed copy of the old contents. The default 2.5-second loop therefore behaves like a compact tape delay. Two additional readers pick small random slices from the same live Buffer and play them at quantized tape-speed ratios, including probabilistic reverse.
There is no capture step: launch Grainloom, play into the input, and the loop starts evolving immediately.

Quick start
- In maiden, run
;install https://github.com/c4pt0r/grainloom. - Restart norns once so SuperCollider discovers the custom engine.
- Set the norns system monitor level to zero.
- Launch Grainloom and send it audio. Recording and replay start automatically.
- Let at least one loop pass, then adjust
feedbackandinput / sample mix. - Raise
tape / slice mixto hear more random slices. - Use K2 to freeze the current Buffer; use K3 to turn sample replay on or off.
- While frozen, hold K2 to dub new layers onto the loop; press K2 again to stop.
Controls
Turn E1 to select a page. E2 and E3 edit its controls.
| Page | E2 | E3 |
|---|---|---|
| LOOP | recording time (20 ms–30 s) | feedback (0–0.98) |
| WINDOW | window start | window size (1–100%) |
| TAPE | tape speed/direction (-2×–2×) | input:sample mix |
| REGEN | regen | regen tone (200 Hz–8 kHz) |
| SLICE | slice size (0.06–0.5 s) | slice density (1–8 Hz) |
| SLICE POS | slice age | slice spread |
| SLICE PLAY | quantized speed ceiling (0.5×–2×) | reverse probability |
| LEVEL | tape:slice mix | sample level (0.25×–4×) |
| BLOOM | bloom | bloom time (0.5–10 s) |
- K2 freeze: a short press freezes/resumes the record head. Playback continues while frozen.
- K2 hold (0.5 s, while frozen): starts dub. The record head runs again, but the existing loop is preserved and new input is layered on top of it instead of crossfading with it. Any press of K2 ends dub and freezes again. Holding K2 while the head is already running does nothing but the usual freeze.
- K3 on/off: turns sample replay on or off. The input side of MIX remains audible.
Because K2 now carries two gestures, its freeze toggle acts on the release
rather than the press. The header and the bottom line follow the state: while
dubbing they read DUB and K2 end dub.

Defaults: 2.5 seconds, 0.72 feedback, the full Buffer as the tape window, normal tape speed, 2:8 input:sample balance, 50% tape:slice mix, 0.2-second slices at 5 Hz, age 0 with full spread, 1.5× quantized speed ceiling, 35% reverse probability, no bloom, 1.5× sample level, and 0.75 output. Every control added after the first release defaults to the behaviour it replaced, so a fresh instance sounds exactly as it did before them. Output level and dub level are available from PARAMETERS rather than an encoder page.
PARAMETERS > PSET saves and recalls every control. The set used last is read back the next time Grainloom loads, before anything is sent to the engine, so saved values are what the engine starts from. With nothing saved yet, nothing changes. A PSET stores controls, not the audio in the Buffer.
The SELECT screen shows a short summary of the instrument and its keys; E2 scrolls it.
Recording time is a wrap point rather than a Buffer size, so changing it takes
effect on the next sample: nothing is reallocated, nothing is cleared, there is
no gap, and it can be swept continuously while playing. Shortening reframes the
loop onto its opening immediately; lengthening extends it with a repeat of what
is already there, which new input then overwrites pass by pass. The engine
allocates once, at load; if it never answers, the header reads ERR and the
keys become usable again. MIX is shown as input:sample: 10:0 is input
only, 5:5 is equal balance, and 0:10 is sample only.
Signal path
input ─> feedback RecordBuf ─> Buffer ─┬─> looping PlayBuf ───────┐
└─> two GrainBuf readers ──┴─> tape/slice mix ─> sample level ─> wet
input ───────────────────────────────────────────────────────────────────────────────────────────────> dry
dry + wet ─> input/sample mix ─> limiter ─> output
The Buffer is twice the longest loop, and the recorder writes every sample twice: once at the head and once one loop ahead of it. Any read that starts inside the loop and runs forward by up to one loop length therefore lands on correctly wrapped audio, so neither the tape reader nor the slice readers need to know where the loop ends. It is also why the loop length can be a plain number both heads wrap on instead of a Buffer that must be rebuilt to resize.
The record path selects the louder hardware input channel rather than summing
left and right, avoiding mono cancellation from opposite-polarity sources. The
choice uses hysteresis, so two channels at similar levels cannot chatter and
leave the selector parked mid-crossfade, summing the very inputs it is there to
keep apart. Input
is DC-filtered, given 2× protected capture makeup, and limited before recording.
Feedback is capped below unity; new input uses 1-feedback, making every write a
bounded crossfade rather than an accumulating gain stage.
Dub is the one place where a write is not a bounded crossfade. The previous
pass is kept at full level and new input is added on top at dub level, so
repeated layers accumulate the way they do on any overdubbing looper; the final
limiter is what keeps the output in range. Ending dub freezes the head where it
stands, so the layer stays aligned with the loop.
Recording time reaches down to 20 ms, which is an audio rate rather than a loop length. Below roughly 50 ms the Buffer stops being a phrase and becomes a waveform: the repetition itself is the pitch, feedback becomes its decay, and dub becomes additive synthesis on top of it. The control is exponential so the short end is reachable.
The tape reader plays a window of the Buffer rather than always the whole of it.
window start places it and window size sets its length, down to one percent;
a small window is a stutter, and opening it up returns to the full loop. The
record head keeps circling the entire Buffer underneath, so a narrow window is
continually refilled with material from outside it. A window that runs past the
end of the Buffer wraps.

The optional slice layer runs two asynchronous, windowed readers whose positions
are anchored to the moving record head. slice age sets how far into the past
they reach and slice spread how wide a span they draw from, so "one second
ago" stays one second ago instead of meaning a fixed spot in the Buffer. At age
0 with full spread the draw is uniform across the whole Buffer, which is what
the readers did before. Every slice chooses a musical tape-speed ratio from 0.5×, 2/3×,
0.75×, 1×, 4/3×, 1.5×, and 2×, limited by quantized speed max;
reverse chance independently reverses slices. Density is capped at 8 Hz per
stream and each reader is capped at eight concurrent grains. Slices mix only into
replay and never feed the record Buffer.
regen folds the tape reader back into the record input, which is what turns
varispeed into accumulation: at any speed but 1× each circulation is re-recorded
shifted, and the loop climbs or sinks through itself. Three things keep that
from running away. regen tone is a lowpass in the fold-back path and never
opens fully, so every generation loses top end and an upward spiral hits a wall
instead of piling up near Nyquist. The 2× capture makeup applies only to the
hardware input, not the fold-back, which would otherwise double the loop gain.
And dub suppresses regen entirely, because dub already retains the whole
previous pass and adding the Buffer's own output on top of that is above unity
by construction. The amount is also capped below one internally.
Turning E1 wraps from the last page back to the first.

bloom makes the machine answer silence. An envelope follower on the input
falls away over bloom time; as it does, slices grow longer, thin out, and take
over more of the tape:slice balance, and they retreat the moment anything is
played. At its default of zero it does nothing at all.
Slices are output-only: they never feed the record Buffer, so increasing slice mix cannot create an uncontrolled feedback stack. The final limiter protects the output from overlapping slice peaks.
There is intentionally no generation-loss, dropout, wow/flutter, bit-crush, glitch, reverb, file loading, or resampling-generation path. Grainloom does not change the global norns reverb.
For MIX to represent the complete dry/wet balance, set the norns system monitor level to zero because system monitoring is a separate dry path.
Influences
Grainloom comes out of two instruments, and knowing them is the fastest way in.
From Make Noise's Morphagene: a window that slides over a reel rather than
playing all of it. window start and window size are Gene Slide and Gene Size;
tape speed is Vari-Speed, forwards and reverse; dub is Sound on Sound; and the
two offset slice readers stagger the way Morph layers genes. What is absent is
just as deliberate -- there are no reels, no splices, no file loading and no
Organize. Morphagene is built around working a recorded reel; Grainloom has one
live loop and no capture step at all.
From Chase Bliss's MOOD: a micro-looper that is simply always recording, with
no gesture to start it, and a loop you can crush down until it stops being a
phrase. recording time is that Clock control taken further at both ends -- up
to 30 seconds, and down to 20 milliseconds, where the loop is no longer a phrase
but a waveform. Freeze is the same idea as holding MOOD's loop.
Two things here belong to neither. slice age anchors the readers to the moving
record head, so a distance into the past stays that distance as the head moves,
instead of naming a fixed spot in the Buffer. And bloom makes the instrument
answer silence, growing into the gaps and withdrawing the moment anything is
played.
Install
Run this command in maiden:
;install https://github.com/c4pt0r/grainloom
After installation, restart norns once so SuperCollider discovers the custom
engine, then select grainloom/grainloom from SELECT. Ordinary Lua-only updates
require only an app reload.
Documentation
License
MIT