Skip to content

Generative Music with Random Values

MIDI Markdown provides random value generation for creating evolving, humanized, and unpredictable musical sequences. The random() function generates different values each time a sequence is played, enabling techniques like humanization (subtle variations), variation (musical variety), and generative patterns (evolving compositions).

Overview

Random expressions inject variation into MIDI sequences, perfect for:

# Humanized velocity variation
[00:00.000]
- note_on 1.60.random(70, 100) 4b

# Varying CC values for evolving pads
[00:02.000]
- cc 1.74.random(30, 90)

# Random notes for generative melodies
[00:04.000]
- note_on 1.random(C3, C5).80 1b

Random expressions complement deterministic modulation (curves, waves, envelopes) by adding controlled unpredictability to otherwise repetitive patterns.


Random Value Basics

Syntax

random(min_value, max_value [, seed=number])

Parameters

  • min_value: Minimum value (integer 0-127 or note name like C3)
  • max_value: Maximum value (integer 0-127 or note name like C5)
  • seed (optional): Reproducible randomness (same seed = same sequence)

Return Type

Always returns an integer MIDI value (0-127 for CC/velocity, 0-127 for note numbers, 12-107 for typical playable notes).

Examples

# Random CC value (0-127)
random(0, 127)

# Random velocity (70-100 for natural dynamics)
random(70, 100)

# Random note (C3 to C5, MIDI 48-72)
random(C3, C5)

# Reproducible random (always same sequence)
random(64, 96, seed=42)

Where Random Works

Velocity (Note Velocity)

Humanize note velocity for natural dynamics:

[00:00.000]
- note_on 1.60.random(60, 100) 1b

[00:01.000]
- note_on 1.64.random(60, 100) 1b

[00:02.000]
- note_on 1.67.random(60, 100) 1b

Each note gets a different random velocity, creating natural-sounding dynamics instead of uniform volumes.

Note Ranges

Generate melodies with random note selection:

[00:00.000]
- note_on 1.random(C4, G4).80 1b

[00:01.000]
- note_on 1.random(C4, G4).80 1b

[00:02.000]
- note_on 1.random(C4, G4).80 1b

Each note is randomly selected from the C4-G4 range, creating unpredictable melodic variation.

CC Values

Vary MIDI CC parameters (cutoff, resonance, pan, etc.):

[00:00.000]
- cc 1.74.random(20, 100)    # Random filter cutoff

[00:02.000]
- cc 1.71.random(30, 80)     # Random resonance

[00:04.000]
- cc 1.10.random(40, 90)     # Random pan position

Perfect for creating variation in filter settings, modulation depth, or any parameter that accepts 0-127.


Where Random DOESN'T Work

Timing and Durations

Random cannot be used for timing values:

# ❌ INVALID - random timing not supported
[+random(100ms, 500ms)]
- note_on 1.60.80 1b

# ❌ INVALID - random duration not supported
- note_on 1.60.80 random(1b, 2b)

Reason: Timing must be deterministic and monotonically increasing for proper MIDI sequencing. Use loops or sweeps for timed variations instead.

Numeric Note IDs in Expressions

Random works with note names but not in all contexts:

# ✅ VALID - note name range
- note_on 1.random(C3, C5).80 1b

# ⚠️ CAUTION - only use integer ranges for direct numbers
- note_on 1.random(48, 72).80 1b   # Works but less readable

# ❌ INVALID - don't mix random with expressions
- note_on 1.(60 + random(0, 12)).80 1b   # Not supported in expressions

Practical Techniques

Humanization

Subtle variations make sequences sound less mechanical:

Dynamic Variation

# Humanize velocity across a drum pattern
[00:00.000]
- note_on 1.36.random(95, 127) 0.5b   # Kick (strong)
- cc 1.10.random(40, 60)               # Pan variation

[00:00.500]
- note_on 2.38.random(70, 90) 0.5b    # Snare (subtle)
- cc 2.10.random(40, 60)

[00:01.000]
- note_on 1.42.random(50, 70) 0.5b    # Hi-hat (light)
- cc 1.10.random(40, 60)

Timing Variations (Groove)

Use velocity variation to simulate timing micro-adjustments:

# Electric bass with subtle dynamics (humanized feel)
[00:00.000]
- note_on 1.36.random(70, 100) 0.5b

[00:00.500]
- note_on 1.41.random(70, 95) 0.5b

[00:01.000]
- note_on 1.43.random(75, 100) 0.5b

Variation Techniques

Evolving Melodies

Generate different melodies each performance:

# Random arpeggio pattern
[00:00.000]
- note_on 1.random(C4, E4).random(60, 100) 0.5b

[00:00.500]
- note_on 1.random(E4, G4).random(60, 100) 0.5b

[00:01.000]
- note_on 1.random(G4, C5).random(60, 100) 0.5b

[00:01.500]
- note_on 1.random(C4, E4).random(60, 100) 0.5b

Random Control Changes

Vary filter, reverb, or effect parameters:

# Evolving filter with random CC value
[00:00.000]
- cc 1.74.random(30, 70)

[00:02.000]
- cc 1.74.random(40, 80)

[00:04.000]
- cc 1.74.random(50, 90)

[00:06.000]
- cc 1.74.random(60, 100)

Generative Patterns

Combine random with loops for procedural generation:

# Generative bass line (different each loop)
@loop 4
  [+1b]
  - note_on 1.random(24, 36).random(90, 127) 0.5b
@end

# Generative arpeggio (varying note range)
@loop 8
  [+0.5b]
  - note_on 1.random(C3, C5).random(70, 100) 0.5b
@end

Combining Random with Modulation

Random adds unpredictability to otherwise smooth modulation. Use together for complex effects:

Random Modulation Base

Start modulation from a random value:

# Filter cutoff starts random, then modulates smoothly
[00:00.000]
- cc 1.74.curve(random(20, 50), 120, ease-in-out)

# Volume swell with random starting point
[00:02.000]
- cc 1.7.curve(random(20, 40), 127, ease-out)

Random Depth with Waveforms

Combine random depth with periodic modulation:

# Random depth LFO (varying intensity)
[00:00.000]
- cc 1.74.wave(sine, 64, freq=2.0, depth=random(10, 40))

# Random frequency with fixed depth (varying speed)
[00:02.000]
- cc 1.7.wave(triangle, 80, freq=random(0.5, 3.0), depth=25)

Random Envelope Parameters

Create variation in envelope shape:

# Varying attack time with random envelope
[00:00.000]
- note_on 1.60.80 2b
- cc 1.74.envelope(ar, attack=random(0.1, 0.5), release=0.5)

# Varying release time
[00:02.000]
- note_on 1.64.80 2b
- cc 1.74.envelope(ar, attack=0.2, release=random(0.3, 1.5))

Stereo Randomization

Random pan and width for stereo effects:

# Random pan with wave modulation
[00:00.000]
[@]
- cc 1.10.wave(sine, random(30, 90), freq=1.5, depth=20)
[@]
- cc 2.10.wave(sine, random(30, 90), freq=1.5, phase=0.5, depth=20)

Reproducibility with Seeds

Deterministic Randomness

Use seed parameter for reproducible sequences:

# Same seed = same random values each time
[00:00.000]
- note_on 1.60.random(60, 100, seed=42) 1b
- cc 1.74.random(30, 100, seed=42)

[00:01.000]
- note_on 1.64.random(60, 100, seed=42) 1b
- cc 1.74.random(30, 100, seed=42)

Using Seeds Strategically

# Different seeds for different sections
@loop 4
  [+4b]
  # Verse uses seed 1 (fixed variation)
  - note_on 1.random(C3, G3).random(70, 100, seed=1) 1b
@end

@loop 4
  [+4b]
  # Chorus uses seed 2 (different fixed variation)
  - note_on 1.random(C4, G4).random(70, 100, seed=2) 1b
@end

Without seeds: Random values change every time you play.

With seeds: Same sequence reproducible across performances (useful for testing, demos, or consistent variations).


Best Practices

Range Selection

Purpose Min-Max Notes
Subtle humanization ±5-10% of normal value Feels natural, less noticeable
Moderate variation ±15-25% range Obvious but still musical
Strong variation ±40-50% range Dramatic, noticeable effect
Extreme variation Full range (0-127) Chaotic, use sparingly

Velocity Humanization Guidelines

# For notes that should be strong
random(80, 127)

# For notes that should be moderate
random(60, 100)

# For light notes (hi-hats, rolls)
random(40, 80)

# For very subtle variation
random(88, 100)

When to Use Random vs. Modulation

Situation Use Random Use Modulation
One-time variation
Humanization
Periodic changes
Smooth transitions
Evolving effects ✅ (both)
Generative patterns ✅ (both)

Common Pitfalls

Don't use random for timing - Causes sequencing issues (use loops instead)

Don't randomize every parameter - Creates chaos, loses musicality

Don't use extreme ranges for velocity - Unnatural dynamics (stay in musical range)

Don't combine too many randoms - Multiple random sources can conflict

DO use seeds for testing - Reproducibility helps debugging

DO layer random with structure - Combine with loops and patterns

DO start subtle - Increase variation gradually until musical


Advanced Techniques

Generative Polyrhythms

Stack loops with different random patterns:

# Generative polyrhythm with multiple random sources
@loop 8
  [+1.5b]
  - note_on 1.random(C4, E4).random(70, 100) 0.5b
@end

@loop 6
  [+2b]
  - note_on 2.random(F3, A3).random(80, 110) 0.5b
@end

Evolving Pad with Random Modulation

# Pad evolves with both random base and wave modulation
[00:00.000]
- note_on 1.48.80 16b
- cc 1.74.wave(sine, random(40, 80), freq=0.3, depth=30)

[00:08.000]
- cc 1.71.curve(random(20, 40), 80, ease-in-out)

Humanized Drum Loop

@define kick_pattern
  [00:00.000]
  - note_on 1.36.random(95, 127) 0.5b
  - cc 1.10.random(45, 55)

  [00:02.000]
  - note_on 1.36.random(85, 110) 0.5b
  - cc 1.10.random(45, 55)
@end

@loop 4
  [+4b]
  @kick_pattern
@end

Random Sweep Depths

Combine random with multiple modulation types:

# Filter cutoff with random envelope and wave
[00:00.000]
- note_on 1.60.80 4b
[@]
- cc 1.74.curve(random(30, 50), 110, ease-out)
[@]
- cc 1.71.wave(sine, random(30, 70), freq=1.0, depth=20)

Performance Considerations

Random Distribution

  • random(min, max) uses uniform distribution (all values equally likely)
  • Generated values are truly random (not pseudo-random seeded by default)
  • With seed=N, sequences are reproducible across runs

CPU Impact

  • Each random() call is very fast (<1ms)
  • Multiple random calls in same event have minimal impact
  • Safe to use in loops without performance concerns

Variation Quantity

# Moderate randomness (musical)
@loop 100
  [+0.25b]
  - note_on 1.60.random(75, 85) 0.25b   # 10-value range
@end

# Extreme randomness (less musical but possible)
@loop 100
  [+0.25b]
  - note_on 1.random(C0, C8).random(0, 127) 0.25b   # Full chaos
@end

Comparison: Random vs. Deterministic

Without Random (Mechanical)

@loop 8
  [+0.5b]
  - note_on 1.60.80 0.5b   # Same every time
  - cc 1.74.70             # Same every time
@end

Result: Identical every performance, lacks humanization.

With Random (Natural)

@loop 8
  [+0.5b]
  - note_on 1.60.random(70, 100) 0.5b   # Different velocity
  - cc 1.74.random(60, 80)               # Different filter
@end

Result: Natural variation, different each performance.

With Seeds (Reproducible Random)

@loop 8
  [+0.5b]
  - note_on 1.60.random(70, 100, seed=1) 0.5b   # Reproducible
  - cc 1.74.random(60, 80, seed=1)               # Reproducible
@end

Result: Same variation every performance (controlled randomness).


Integration Examples

Humanized Synth Line

---
tempo: 120
time_signature: 4/4
---

[00:00.000]
- note_on 1.60.random(75, 95) 1b
- cc 1.74.curve(random(40, 60), 80, ease-out)

[00:01.000]
- note_on 1.62.random(75, 95) 1b
- cc 1.74.curve(random(45, 65), 80, ease-out)

[00:02.000]
- note_on 1.65.random(75, 95) 1b
- cc 1.74.curve(random(50, 70), 80, ease-out)

[00:03.000]
- note_on 1.67.random(75, 95) 1b
- cc 1.74.curve(random(55, 75), 80, ease-out)

Generative Ambient Pad

---
tempo: 60
---

# Base pad with evolving filter
[00:00.000]
- note_on 1.48.60 120b
- cc 1.74.wave(sine, random(50, 70), freq=0.2, depth=40)

# Add resonance variation
[00:04.000]
- cc 1.71.curve(random(20, 40), random(60, 80), ease-in-out)

# Auto-pan with random center
[00:08.000]
- cc 1.10.wave(sine, random(30, 90), freq=0.5, depth=50)

Percussive Loop with Humanization

@loop 4
  # Kick with humanized velocity and filter variation
  [00:00.000]
  - note_on 1.36.random(100, 127) 0.5b
  - cc 1.74.random(80, 127)

  # Snare with velocity variation
  [00:02.000]
  - note_on 2.38.random(80, 100) 0.5b

  # Hi-hat with varying pan
  [00:02.500]
  - note_on 1.42.random(60, 80) 0.25b
  - cc 1.10.random(40, 90)
@end

Troubleshooting

Random values seem the same

Issue: Using random(min, max) without seed can sometimes appear repetitive due to random variation.

Solution: Use seed=N if you need consistent variation testing, or accept the natural randomness.

Range is too wide

Issue: random(0, 127) for velocity sounds unnatural (jumps between ppp and fff).

Solution: Use narrower ranges like random(70, 90) for more subtle, musical variation.

Random notes are out of key

Issue: random(C0, C8) generates unwanted low/high notes.

Solution: Specify exact range: random(C3, C5) or random(48, 72).

Can't use random in timing

Issue: Trying [+random(100ms, 500ms)] causes error.

Solution: Use loops with fixed timing instead. Random timing is not supported for sequencing safety.


See Also

Complete Working Examples

Six comprehensive examples demonstrating all random() techniques:

See Examples Guide for learning paths and feature matrix.


Reference

Random Syntax Summary

# Integer range
random(0, 127)
random(64, 96)

# Note name range
random(C3, C5)
random(C#4, E5)
random(Bb3, G4)

# With reproducible seed
random(70, 100, seed=42)
random(C4, G4, seed=1)

# In context
- note_on {ch}.random(C3, C5).{velocity} 1b
- cc {ch}.{number}.random(30, 100)
- note_on {ch}.{note}.random(60, 100) 0.5b

Supported Contexts

Context Example Status
Velocity note_on 1.60.random(70, 100) 1b ✅ Works
Note note_on 1.random(C3, C5).80 1b ✅ Works
CC Value cc 1.74.random(30, 100) ✅ Works
Pitch Bend pitch_bend 1.random(0, 16384) ✅ Works
Timing [+random(100ms, 500ms)] ❌ Not Supported
Duration note_on 1.60.80 random(1b, 4b) ❌ Not Supported