| project name: | thorns |
| project url: | https://github.com/mpelath/thorns |
| author: | mpelath |
| description: | fractal sequencer for norns + grid |
| discussion url: | https://llllllll.co/t/thorns-a-fractal-sequencer/73644 |
| documentation url: | https://github.com/mpelath/thorns/blob/main/README.md |
| tags: | norns grid midi sequencer generative |
Fractal MIDI sequencer for norns + grid
Edit Mode (Stopped): - Screen 1 (Gate/Pitch): Click cells to set/clear notes - Columns = steps (1-16) - Rows = pitches (8 visible at a time from 3-octave range) - Scale degrees shown dimly - Screen 2 (Velocity): Click column to set velocity height - Vertical bar height = velocity (8 levels)
Play Mode: - Displays currently playing transformed sequence - Current step highlighted - Shows notes in current octave window (use K1+E1 to shift view) - Read-only display
When you hit play with a modified trunk: 1. Generates a complete 7-level binary tree (always maximum depth) 2. At each branch level: - Pitch modification and velocity modification are checked against their probabilities - Time shift, gate chaos, and mutation are always applied (but have their own control parameters) 3. Multiple transformations can be applied to the same branch (they stack in sequence) 4. Pre-generates all random values (so playback is deterministic) 5. Stores full sequences at every node
The Branches parameter (0-7) controls how deep into the tree you play before looping back to the trunk. The tree is always generated at full depth, so you can adjust Branches in real-time during playback without regenerating.
Control parameters: - Pitch/Velocity Mod Prob: 0.0 = never applies, 1.0 = always applies - Shift Freedom: 0.0 = never shifts, 1.0 = all shift amounts equally likely - Gate Chaos/Mutation Prob: per-step probabilities (even when transformation "always applies")
Example: If Branches = 2 and Path = 0.3: - Path 0.3 falls in range [0.25, 0.375) = binary "01" - Playback sequence: trunk → left branch → right branch → loop
1. Pitch Modification
- Nonlinear pitch transformation using power function
- For each note: r = pitch relative to base (-8 to 15), p = (r - 3.5) / 12
- Random exponent g between 1 and 2
- If p > 0: transformed_p = p^g
- If p < 0: transformed_p = -(-p)^g
- Transform back: r = 3.5 + 12 × transformed_p
- Branch A: uses exponent g (expands high/low ranges)
- Branch B: uses exponent 1/g (compresses toward center)
2. Velocity Modification - Nonlinear velocity transformation using power function - For each note: v = velocity (0-127), p = (v - 63.5) / 64 - Random exponent g between 1 and 10 - If p > 0: transformed_p = p^g - If p < 0: transformed_p = -(-p)^g - Transform back: v = 63.5 + 64 × transformed_p - Branch A: uses exponent g (expands dynamic range) - Branch B: uses exponent 1/g (compresses toward middle velocity)
3. Mutate - Randomly shifts both pitch and velocity for each step - Pitch: shifts by -1, 0, or +1 semitone with probabilities: -1 (p/2), 0 (1-p), +1 (p/2) where p = mutation probability - Pitch bounces off range limits (-8 to +15 semitones relative to base pitch) - Velocity: shifts by -8, 0, or +8 units with same probability distribution - Velocity wraps around range (1-127) - Branch A: Apply shifts as generated - Branch B: Apply negated shifts
4. Gate Chaos - Each step has probability p (gate chaos probability) to: - If gate on: turn off - If gate off but was originally on: turn on - If gate off and was never on: create new random note - Both branches apply same logic to same steps
5. Time Shift - Shifts pattern by m steps where m is chosen from 0 to pattern_length using weighted distribution - Distribution: P(m) = x^m · (x-1)/(x^(n+1)-1) where x = shift_freedom parameter - Special case: if m = pattern_length, reverse the sequence instead of rotating - Branch A: Rotate forward by m steps (or reverse if m = n) - Branch B: Rotate backward by m steps (or reverse if m = n - reverse is its own inverse) - Shift freedom controls distribution: - x = 0: always m = 0 (no shift) - x = 1: uniform distribution over all m - 0 < x < 1: favors smaller shifts - Preserves all note data, changes timing/position/order
All random values are pre-generated when tree is built, so the same path always produces the same result.
Thorns requires the nb (nota bene) library and at least one nb voice. Install voices using maiden:
;install https://github.com/sixolet/emplaitress
Recommended voices to start:
- emplaitress - Polyphonic MI Plaits (versatile, many synthesis modes)
- doubledecker - 2-layer synth (CS-80 style)
- polyperc - PolyPerc engine
Enable voices in SYSTEM > MODS then SYSTEM > RESTART.
THORNS from the norns script selectionPARAMETERS > Voice and select an installed nb voiceInspired by Qu-Bit Electronix Bloom hardware sequencer. This is an independent implementation with different features and approach. Not affiliated with or endorsed by Qu-Bit Electronix.
Built for norns.
Copyright (c) 2025 Marc Pelath
This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with this program. If not, see https://www.gnu.org/licenses/.