Morphic Form — Grain Placement Engine — User Guide

Spectral-preserving grain placement system inspired by attractor dynamics and gradient descent. Controls ONLY grain placement parameters (size, density, jitter, backtrack, repeat) while leaving spectral content untouched.

Author: Shai Cohen Affiliation: Department of Music, Bar-Ilan University, Israel Version: 1.1 (2025) Citation: Cohen, S. (2025). Praat AudioTools Repo: https://github.com/ShaiCohen-ops/Praat-plugin_AudioTools
Contents:

What this does

This script implements a Morphic Form grain placement engine — a system that generates new sonic structures by controlling ONLY the placement parameters of grains extracted from a source sound. Unlike granular synthesis that alters spectral content, this engine preserves the original spectral character completely, reshaping only when grains occur, how long they are, how much jitter they have, and whether they repeat or backtrack.

🌀 What is Morphic Form?

Morphic Form is a conceptual model where two behavioral attractors define extremes of grain placement:

  • Attractor A (Stable / Sparse / Coherent): larger grains, lower density, low jitter, mostly forward motion, rare repeats, occasional silence gaps
  • Attractor B (Turbulent / Dense / Unstable): shorter grains, higher density, high jitter, frequent backtracks and repeats, no silence gaps

A gradient descent state walks smoothly from A toward B over the output duration, modulated frame-by-frame by local acoustic features (intensity, activity, voicing) extracted from the original. The output can be shorter or longer than the source via decoupled time ratios.

Key Features:

Technical Implementation: (1) Feature Extraction: Intensity, voicing, and frame-to-frame activity from source (mapped to output timeline). (2) Morphic Curve: Gestural pacing (Linear/Accelerate/Decelerate) scaled by morph_intensity. (3) Attractor Definitions: A and B parameters derived from base values. (4) Gradient Descent: Per-frame state update with local feature push and inertia. (5) Grain Schedule: Generate grains walking output timeline, reading source with time ratio and wrapping. (6) Assembly: Extract grains with 10% fades, concatenate with crossfades between grains. (7) Visualization: 6-panel display with all analysis data.

Quick start

  1. In Praat, select exactly one Sound object (any duration, any content).
  2. Run script… → select Morphic_Form.praat.
  3. Choose Preset (2-7 for specific strategies, 1 for custom).
  4. Adjust morphic control parameters (morph intensity, base grain, base density, max jitter).
  5. Set output duration ratio (1.0 = same as input, 2.0 = double, 0.5 = half).
  6. Select pacing curve (Linear, Accelerate, Decelerate).
  7. Enable Draw_visualization for analysis display.
  8. Click OK — engine extracts features, generates grain schedule, assembles output.
Quick tip: Start with Slow Bloom preset on a 10-20 second recording with varied dynamics. Enable visualization — you'll see the morph curve (red) gradually rising from A to B, grain size (green) decreasing, density (blue) increasing. Listen to how the texture transforms from sparse, stable grains to denser, more turbulent placement. The output appears as "original_morphic" in the Objects window.
Important: SPECTRAL CONTENT IS PRESERVED — Only grain placement changes; pitch and timbre remain exactly as in source. OUTPUT DURATION RATIO > 1.0 causes source reading to wrap (repeats), < 1.0 causes time compression (skips material). MAX_GRAINS automatically scales with output duration (up to 6000). GRASS GRAINS < 10 ms are skipped. VISUALIZATION may be slow for >2000 grains; grain map downsampled to 400 points.

Morphic Form Theory

The Two-Attractor Model

🎯 Attractor Definitions

ParameterAttractor A (Stable)Attractor B (Turbulent)
Grain sizebase_grain_ms × 1.35base_grain_ms × 0.67
Density (grains/s)base_density_gps × 0.55base_density_gps × 1.90
Jitter (ms)max_jitter_ms × 0.08max_jitter_ms
Backtrack probability0.020.22
Repeat probability0.040.28
Silence probability0.070.00
Silence duration (ms)40.00.0

Conceptual meaning:

  • A: Coherent, predictable, spacious — like a stable texture or sustained tone
  • B: Chaotic, dense, unstable — like turbulent noise or rapid-fire gestures

Gradient Descent State

📉 State Update Equation

For each analysis frame i:

target_m = morph_curve[i] (0→1 from pacing curve × morph_intensity) local_push = 0.55 × activity[i] + 0.45 × (1 - voicing[i]) adj_target = target_m + local_weight × local_push adj_target = clamp(adj_target, 0, 1) gd_err = adj_target - gd_state gd_state = gd_state + gd_lr × gd_err gd_state = clamp(gd_state, 0, 1) m = gd_state (final attractor blend factor)

Parameters:

  • gd_lr = 0.22 — learning rate (inertia) — higher = faster response
  • local_weight = 0.30 — how much local features affect target
  • activity = normalized frame-to-frame intensity change
  • voicing = 1 if pitch detected, 0 otherwise

Effect: The state smoothly tracks the target while being pulled toward regions of high activity or unvoiced sound, creating a responsive, living texture.

Pacing Curves (Gestural Accumulator)

Linear: t_eased = t_norm Even pacing throughout duration Accelerate: t_eased = t_norm² Slow start, accelerating toward the end (rush to B) Decelerate: t_eased = 1 - (1 - t_norm)² Fast start, then stabilization (explosive beginning) morph_curve[i] = t_eased × morph_intensity

Output Duration Control

time_ratio = src_dur / out_dur For each output time t_out: src_base = t_out × time_ratio while src_base ≥ src_dur: src_base = src_base - src_dur (wrap) src_pos = src_base + jitter ± backtrack Interpretation: • ratio = 1.0: proportional read (no wrap) • ratio > 1.0: stretched output — source read slower, wraps to repeat • ratio < 1.0: compressed output — source read faster, skips material The grain read map visualization shows this relationship.

Grain Schedule Generation

For each output step: • Determine current analysis frame from t_out • Look up interpolated parameters: grain_ms, density, jitter, bt_prob, rep_prob, sil_prob • hop_s = 1.0 / density (time between grain starts) • src_pos = t_out × time_ratio + gauss(0, jitter) • If backtrack triggered (random < bt_prob): src_pos -= random(0.04, 0.22) • If repeat triggered (random < rep_prob): src_pos = last_src • Clamp src_pos to [0, src_dur - grain_s] • If silence triggered (random < sil_prob): insert silence grain Max grains = min(6000, floor(out_dur × 80))

Preset Strategies

Preset 2: Slow Bloom (gentle A→B, large grains)

🌸 Gradual Unfolding

Morph: 0.60 | Base grain: 52 ms | Density: 17 g/s

Jitter: 7 ms | Pacing: Linear | Ratio: 1.0×

Character: Gentle transition from stable, spacious texture to moderately denser placement

Use on: Pads, drones, ambient material

Preset 3: Tidal Surge (aggressive A→B, accelerating)

🌊 Building Momentum

Morph: 0.90 | Base grain: 30 ms | Density: 38 g/s

Jitter: 28 ms | Pacing: Accelerate | Ratio: 1.2×

Character: Slow start, then rapid acceleration to turbulent, dense placement with high jitter

Use on: Buildups, crescendos, dramatic gestures

Preset 4: Nervous Scatter (B-dominant, dense + jittery)

⚡ Chaotic Dispersion

Morph: 0.95 | Base grain: 22 ms | Density: 52 g/s

Jitter: 40 ms | Pacing: Linear | Ratio: 0.85×

Character: Immediately turbulent — high density, extreme jitter, compressed duration

Use on: Glitch, percussion, chaotic textures

Preset 5: Still Center (A-dominant, minimal morphing)

🧘 Stable Core

Morph: 0.15 | Base grain: 68 ms | Density: 11 g/s

Jitter: 4 ms | Pacing: Decelerate | Ratio: 1.0×

Character: Mostly stable throughout — large grains, low density, minimal jitter

Use on: Sustained tones, meditation, background textures

Preset 6: Time Stretch (2× duration, slow drift)

⏱️ Temporal Expansion

Morph: 0.45 | Base grain: 60 ms | Density: 20 g/s

Jitter: 12 ms | Pacing: Decelerate | Ratio: 2.0×

Character: Double duration with slow morph — source material wraps, creating repetitions

Use on: Ambient expansion, time dilation effects

Preset 7: Collapse (half duration, rapid compression)

📉 Temporal Compression

Morph: 0.80 | Base grain: 25 ms | Density: 45 g/s

Jitter: 20 ms | Pacing: Accelerate | Ratio: 0.5×

Character: Half duration, rapid morph — source material compressed, creating density

Use on: Transitions, quick gestures, compression effects

Parameters & Controls

Morphic Control

ParameterDefaultDescription
Morph_intensity0.750.0 = stay at A entirely | 1.0 = fully reach B
Base_grain_ms40.0Reference grain duration (ms) — scaled for A/B
Base_density_gps25.0Reference density (grains/second) — scaled for A/B
Max_jitter_ms15.0Maximum jitter at attractor B (ms)

Duration

ParameterDefaultDescription
Output_duration_ratio1.01.0 = same as input, 2.0 = double, 0.5 = half

Pacing Curve

ParameterDefaultDescription
Pacing_curveLinearLinear, Accelerate (slow start), Decelerate (fast start)

Fixed Parameters (internal)

ParameterValueDescription
gd_lr0.22Gradient descent learning rate (inertia)
local_weight0.30Weight of local feature modulation
af_hop_s0.050Analysis frame hop (seconds)
max_grainsmin(6000, out_dur×80)Maximum grains to generate
fade_percent10%Grain fade in/out length
xfade_max20 msMaximum crossfade between grains

Output

ParameterDefaultDescription
Draw_visualization1Generate 6-panel analysis display
Play_result1Audition after processing

Visualization & Analysis

6-Panel Display

Morphic Form Visualization: Panel 1: TITLE • Script name, preset, morph intensity, pacing curve, ratio, grain count Panel 2: ORIGINAL WAVEFORM (LEFT) • Gray waveform of source • Label: "Original (duration)" Panel 3: OUTPUT WAVEFORM (RIGHT) • Blue waveform of morphed output • Label: "Morphic (duration)" Panel 4: MORPH CURVE + GRAIN SIZE • X-axis: Output time, Y-axis: 0-1 • Light blue filled area = morph curve (target) • Red line = morph curve (actual target) • Green line = normalized grain size (1=A, 0=B) • Label: "Morph Curve + Grain Size" Panel 5: DENSITY + JITTER • X-axis: Output time, Y-axis: grains/second • Blue line = density • Orange line = jitter (scaled to density axis) • Label: "Density + Jitter" Panel 6: FEATURE TRAJECTORIES • X-axis: Output time, Y-axis: normalized 0-1 • Light blue shaded regions = voiced frames • Blue line = normalized intensity • Red line = activity (frame-to-frame change) • Green line = voicing (scaled to 0.92) • Grid midline at 0.5 • Label: "Features (voiced regions shaded)" Panel 7: GRAIN READ MAP • X-axis: Output time (s) • Y-axis: Source time (s) • Gray line = proportional read reference (diagonal with wraps) • Colored dots = individual grains (color gradient: early=blue, late=orange) • Label: "Grain Read Map (ref line = proportional read)" Panel 8: STATS PANEL • Source name, durations, ratio, grain count • Attractor A and B parameter values • Morph intensity, pacing, GD parameters • Base grain, density, jitter Panel 9: LEGEND • Color key for all plot elements

Reading the Grain Read Map

What the scatter plot shows:
  • Each dot: A single grain, plotted at its output time (x) and source read position (y)
  • Color gradient: Early grains (blue) to late grains (orange) — shows temporal evolution
  • Gray reference line: Proportional read (diagonal with wraps) — where grains would be if reading source linearly
  • Vertical scatter: Jitter — grains reading from different source positions at same output time
  • Backtracking: Dots appearing earlier in source than previous grains at same output time
  • Wraps: For ratio > 1.0, dots wrap from bottom to top when source repeats

Reading the Morph Curve Panel

What the curves tell you:
  • Light blue fill: Target morph curve (from pacing curve × morph_intensity)
  • Red line: Actual morph curve (may differ due to feature modulation)
  • Green line: Grain size normalized (1 = attractor A, 0 = attractor B) — should roughly follow inverse of morph
  • Divergence: Where red and blue differ, local features are modulating the target

Applications

Electroacoustic Composition

Use case: Generating evolving textures from static source material

Technique: Slow Bloom or Tidal Surge presets on sustained sounds

Workflow:

Rhythmic Variation

Use case: Creating rhythmic variations from percussive loops

Technique: Nervous Scatter or Collapse presets on drum loops

Settings:

Result: Percussive material rearranged into new rhythmic patterns while preserving timbre

Time Dilation/Compression

Use case: Creating extended or condensed versions of material

Technique: Time Stretch or Collapse presets

Applications:

Research & Education

Use case: Demonstrating attractor dynamics and gradient descent in audio

Technique: Enable visualization, compare presets on simple test signals

Learning outcomes:

Practical Workflow Examples

🎬 Film Score: Tension Buildup

Goal: Create 30-second tension cue from 10-second drone

Settings:

  • Source: 10-second low drone
  • Preset: Tidal Surge
  • Custom: ratio = 3.0× (30s output), morph_intensity = 0.95
  • Pacing: Accelerate

Result: Drone gradually transforms from stable to turbulent over 30 seconds, with source material wrapping and repeating

🎚️ Electronic Music: Glitch Break

Goal: Create glitchy break from 4-bar drum loop

Settings:

  • Source: 8-second drum loop
  • Preset: Nervous Scatter
  • Base_grain_ms: 25 (short for detail)
  • Ratio: 1.0

Result: Drum hits scattered into dense, jittery texture — perfect for glitch transitions

🎙️ Voice Processing: Textural Voice

Goal: Transform spoken phrase into abstract texture

Settings:

  • Source: 5-second spoken phrase
  • Preset: Slow Bloom
  • Base_grain_ms: 60 (preserves phoneme length)
  • Ratio: 2.0× (10-second output)

Result: Voice becomes abstract texture — words dissolve into grain clouds while preserving spectral character

Troubleshooting Common Issues

Problem: No grains generated
Cause: density too low or grain size too large for source duration
Solution: Increase base_density_gps, reduce base_grain_ms, or use shorter source
Problem: Output much shorter/longer than target
Cause: Grain count insufficient to fill duration, or pad/trim not applied
Solution: Check max_grains scaling, increase density, or manually adjust ratio
Problem: Clicks between grains
Cause: Fades too short or crossfades not applied correctly
Solution: Increase fade percentage in script (currently 10%), ensure crossfade applied for grain+grain transitions
Problem: Grain read map shows no jitter/backtracking
Cause: Morph intensity too low, or attractor B parameters not reached
Solution: Increase morph_intensity, check that max_jitter_ms is >0, increase base_density_gps
Problem: Visualization very slow
Cause: Many grains (>2000) and high-resolution plotting
Solution: Reduce grain count (lower density), or disable visualization for final render

Advanced Techniques

Custom attractor definitions (edit script):
  • Extreme A: Increase att_a_grain_ms (×2.0), decrease att_a_density (×0.3), set att_a_sil_prob = 0.2
  • Extreme B: Decrease att_b_grain_ms (×0.4), increase att_b_density (×3.0), set att_b_bt_prob = 0.5, att_b_rep_prob = 0.5
  • No silences: Set att_a_sil_prob = 0.0
  • No backtracking: Set att_b_bt_prob = 0.0
Feature modulation weights:

Modify local_push calculation to emphasize different features. For example, increase activity weight for more responsiveness to transients, or decrease voicing weight for less pitch influence.

Gradient descent parameters:
  • gd_lr: Higher (0.3-0.5) = faster response but more oscillation
  • local_weight: Higher (0.5-0.7) = more feature influence, less smoothness
Output duration extreme ratios:

Ratios > 5.0 or < 0.2 may produce extreme results (many wraps or severe skipping). Useful for experimental textures.