Sanic Boom

Patterns

Sequences and pattern manipulation

Patterns are the core of Sanic. They define sequences of notes that play over time.

Basic Patterns

[C4 E4 G4]           // Three quarter notes
[C4 C4 C4 C4]        // Four quarter notes
[C4 _ _ _]           // One note, three rests

Notes in a sequence are played evenly across one bar (4 beats).

Note Durations (Weights)

Use :weight after a note to make it take more time relative to other notes:

// C4 takes twice the time of E4 and G4
[C4:2 E4 G4]    // C4: 50%, E4: 25%, G4: 25%

// Dotted rhythm (3 + 1)
[C4:3 E4]       // C4: 75%, E4: 25%

// Long rest between notes
[C4 _:2 E4]     // C4: 25%, rest: 50%, E4: 25%

Weights are relative. [C4:2 E4 G4] has total weight 4 (2+1+1).

Per-Note Probability

Add ? after a note for probability-based triggering. Notes may randomly not play:

[C4? E4 G4?]           // C4 and G4 have 50% chance (default)
[C4?0.3 E4 G4?0.7]     // C4 has 30%, G4 has 70% chance
[_?0.5]                // Rest with 50% chance (fills with silence or note)
[Cm7?0.5]              // Chord with probability

Great for adding variation and organic feel to patterns.

Per-Note Velocity

Add @ after a note to control its volume/intensity (0.0-1.0):

[C4@0.8 E4@0.5 G4@1.0] // Explicit velocities
[C4@0.3 E4 G4]         // E4 and G4 use default (1.0)
[Cm7@0.6]              // Chord with velocity

Combined Modifiers

Modifiers can be combined. Order: Note:weight?probability@velocity

[C4:2?0.5@0.8 E4 G4]   // Double duration, 50% chance, 80% velocity
[C4?0.5@0.8 E4@0.6?0.3 G4]  // Order of ? and @ is flexible

Microtonal Patterns

Add + or - cents after a note for microtonal pitch control (100 cents = 1 semitone):

// Quarter-tone Arabic scale (Hijaz-ish)
let hijaz = [D4 Eb4+50 F#4 G4 A4 Bb4+50 C5 D5]
hijaz |> saw |> lowpass(800)

// Just intonation approximations
let pure = [C4 E4-14 G4+2 C5]   // Pure major third, pure fifth
pure |> sine

// Direct frequency control
let drone = [432hz 432hz 432hz 432hz]
drone |> sine |> gain(0.3)

Cent offsets work with all modifiers: C4+50:2@0.8?0.5

Nested Subdivisions

Use nested brackets to create uneven rhythms:

// C4 gets half, [E4 G4] gets half (E4 and G4 each get quarter)
[C4 [E4 G4]]

// Deep nesting: C4: 1/2, E4: 1/4, [G4 B4]: 1/4
[C4 [E4 [G4 B4]]]

This is useful for syncopated rhythms and polyrhythms.

Chords

Play multiple notes simultaneously by naming chords:

[Cm7 Fmaj7 G7]       // Chord progression
[Am _ Dm _]          // Chords with rests

Or use the chord function:

chord("Cm7")         // Returns [C4 Eb4 G4 Bb4]
chord("Fmaj7", 3)    // F major 7 in octave 3

Power Chords

Power chords consist of just root + perfect 5th (7 semitones up). Without the 3rd, they're neither major nor minor—just raw power. Essential for rock, metal, punk, and industrial music. Use pow or p5 suffix:

[Epow Apow Dpow Apow]     // Classic rock progression
[E2pow A2pow D3pow]       // With explicit octaves
[Ep5 _ Ap5 _]             // Alternative syntax

Power Chord Reference

ChordNotesInterval
EpowE4, B4Root + P5
E2powE2, B2Root + P5 (octave 2)
ApowA4, E5Root + P5
DbpowDb4, Ab4Root + P5

Classic Power Chord Sounds

// Punk rock - fast strumming
let punk = [Epow Epow Apow Apow | Dpow Dpow Epow Epow]
    |> saw |> distortion(0.7) |> fast(2)

// Metal chug - palm muted style with gate
let metal = [E2pow _ E2pow _ | G2pow A2pow E2pow _]
    |> saw |> distortion(0.9) |> gate(pattern: "xxx_xx_x", rate: 1/16)

// Industrial - with ring mod for metallic texture
let industrial = [E2pow _ E2pow G2pow | A2pow _ E2pow _]
    |> saw |> distortion(0.8) |> ring(freq: 150, mix: 0.3)

// Grunge - loose and heavy
let grunge = [Epow _ _ Gpow | Apow _ Epow _]
    |> supersaw |> distortion(0.6) |> lowpass(2000) |> reverb(0.3)

Pattern Transforms

Transform patterns with built-in functions:

Speed

[C4 E4 G4] |> fast(2)    // Play twice as fast
[C4 E4 G4] |> slow(2)    // Play twice as slow

Reverse

[C4 E4 G4] |> reverse    // G4 E4 C4

Shuffle

Randomly reorder notes (different each cycle):

[C4 E4 G4 B4] |> shuffle |> saw

Rotate

[C4 E4 G4 B4] |> rotate(1)   // E4 G4 B4 C4
[C4 E4 G4 B4] |> rotate(-1)  // B4 C4 E4 G4

Transpose

[C4 E4 G4] |> transpose(7)   // Up a fifth
[C4 E4 G4] |> transpose(-12) // Down an octave

Conditional Transforms

Apply transforms conditionally:

Every N Cycles

[C4 E4 G4] |> every(4, reverse)      // Reverse every 4th cycle
[C4 E4 G4] |> every(2, transpose(7)) // Transpose every other cycle

Probability

[C4 E4 G4] |> prob(0.5, transpose(12))  // 50% chance to transpose
[C4 E4 G4] |> degrade(0.3)              // 30% chance to skip each note

Convenience functions for common probabilities:

[C4 E4] |> sometimes(reverse)      // 50% probability
[C4 E4] |> often(reverse)          // 75% probability
[C4 E4] |> rarely(reverse)         // 25% probability
[C4 E4] |> almostAlways(reverse)   // 90% probability
[C4 E4] |> almostNever(reverse)    // 10% probability

Combining Patterns

Stack (Parallel)

Play patterns simultaneously:

let drums = [kick _ snare _]
let bass = [C2 _ _ C2]

play stack(drums, bass)

Cat (Sequential)

Play patterns one after another:

let intro = [C4 E4]
let verse = [G4 B4]

play cat(intro, verse)

Repeat

Repeat a pattern n times:

play repeat(4, [C4 E4]) |> saw  // Plays [C4 E4] 4 times

Choose

Randomly pick one pattern each cycle:

let a = [C4 D4 E4 F4]
let b = [G4 A4 B4 C5]

play choose(a, b) |> saw  // Randomly plays a or b each cycle

Arpeggiator

The arp() function reorders notes in a pattern based on a direction. Works on chords and any pattern.

Basic Directions

play Cm7 |> arp(up) |> saw       // C, Eb, G, Bb (low to high)
play Cm7 |> arp(down) |> saw     // Bb, G, Eb, C (high to low)
play Cm7 |> arp(random) |> saw   // Random order each cycle

Bouncing Directions

play Cm7 |> arp(updown) |> saw   // C, Eb, G, Bb, G, Eb (bounce up then down)
play Cm7 |> arp(downup) |> saw   // Bb, G, Eb, C, Eb, G (bounce down then up)

Alternating Directions

play Cm7 |> arp(outside) |> saw  // C, Bb, Eb, G (alternate lowest/highest)
play Cm7 |> arp(inside) |> saw   // Bb, C, G, Eb (alternate highest/lowest)

Converging/Diverging

play Cm7 |> arp(converge) |> saw // C, Bb, Eb, G (pairs from outside in)
play Cm7 |> arp(diverge) |> saw  // Eb, G, C, Bb (start middle, expand out)

All Arp Modes

ModePattern (for C E G B)Description
upC E G BLow to high
downB G E CHigh to low
random(varies)Random each cycle
updownC E G B G EBounce up then down
downupB G E C E GBounce down then up
outsideC B E GAlternate outer notes
insideB C G EAlternate inner notes
convergeC B E GPairs moving inward
divergeE G C BStart center, expand

Works on any pattern, not just chords:

play [C4 D4 E4 F4 G4] |> arp(outside) |> saw
play [C4 E4 G4 B4] |> arp(updown) |> fast(2) |> saw

Euclidean Rhythms

Generate mathematically distributed rhythms with euclid(hits, steps):

play [C4] |> euclid(3, 8) |> saw   // Classic "tresillo" rhythm
play [C4] |> euclid(5, 8) |> saw   // Cinquillo pattern
play [C4] |> euclid(7, 16) |> saw  // More complex rhythm

Common Euclidean patterns:

PatternName
euclid(2, 3)Basic triplet feel
euclid(3, 4)Cumbia
euclid(3, 8)Tresillo (Cuban)
euclid(5, 8)Cinquillo
euclid(7, 12)West African bell

With rotation:

play [C4] |> euclid(3, 8, 1) |> saw    // Rotated by 1 step

Swing

Add swing feel:

[C4 C4 C4 C4] |> swing(0.6)  // 60% swing

Humanize

Adds random velocity variation for a more human feel:

[C4 E4 G4] |> humanize()      // ±10% variation (default)
[C4 E4 G4] |> humanize(0.2)   // ±20% variation

Polymeters with over()

Sets the pattern's cycle duration for polymeters:

tempo 120

let a = [C4 E4 G4 B4]           // 4 beats (default bar)
let b = [C4 E4 G4] |> over(3)   // 3 beats
let c = [C4 E4] |> over(2)      // 2 beats

play stack(a, b, c)  // Patterns drift and realign

// Fractional durations
[C4 E4 G4] |> over(3/4)   // 0.75 beats (triplet feel)

On this page