Generative Art

Expression-Based Systems

Generative art emerges from systems—simple rules that create complex, evolving visual output. In After Effects, expressions become your generative engine: mathematical functions, noise fields, recursive patterns, and emergent behaviors that produce art you couldn't design by hand. These techniques reward experimentation—small parameter changes yield dramatically different results.

1. Fractal Branching Systems

Create self-similar branching structures that evolve over time. Apply to a shape layer's path or use with the Repeater to generate tree-like forms, neural networks, or organic growth patterns.

// Apply to Shape Layer > Contents > Shape > Path
// Creates evolving fractal branch pattern

iterations = 5;        // Depth of recursion
angle = 25;            // Branch angle (degrees)
shrink = 0.7;          // Size reduction per iteration
growth = time * 30;    // Animation speed

function branch(len, iter) {
    if (iter <= 0) return [];
    
    points = [];
    end = [0, -len];
    points.push([0,0], end);
    
    // Left branch
    leftPoints = branch(len * shrink, iter - 1);
    for (i = 0; i < leftPoints.length; i++) {
        rotated = rotatePoint(leftPoints[i], -angle);
        points.push(add(rotated, end));
    }
    
    // Right branch
    rightPoints = branch(len * shrink, iter - 1);
    for (i = 0; i < rightPoints.length; i++) {
        rotated = rotatePoint(rightPoints[i], angle);
        points.push(add(rotated, end));
    }
    
    return points;
}

function rotatePoint(p, a) {
    rad = degreesToRadians(a);
    return [
        p[0] * Math.cos(rad) - p[1] * Math.sin(rad),
        p[0] * Math.sin(rad) + p[1] * Math.cos(rad)
    ];
}

// Animate growth
animatedLen = 100 * Math.min(growth / 100, 1);
branch(animatedLen, iterations);
Try These Variations
  • Change angle to 60° for snowflake patterns
  • Add wiggle(0.5, 5) to angles for organic wind effect
  • Use noise(time) to drive angle changes over time
  • Layer multiple shape layers with different parameters

2. Flow Fields with Perlin Noise

Create particle systems that follow invisible force fields. Each particle reads its direction from a noise field, producing organic, river-like motion. Perfect for abstract fluid art, smoke trails, or ethereal energy flows.

// Apply to Position of multiple layers
// Each layer gets unique seed via index

seed = index * 100;           // Unique per layer
scale = 0.003;                // Noise scale (smaller = smoother)
speed = 50;                   // Movement speed

// Get current position
pos = value;

// Sample noise field at position
noiseVal = noise(pos[0] * scale + seed, pos[1] * scale + time * 0.1);

// Convert noise to angle (0-360 degrees)
angle = noiseVal * Math.PI * 4;

// Calculate velocity
velocity = [Math.cos(angle) * speed * 0.016, Math.sin(angle) * speed * 0.016];

// Apply movement
pos + velocity;

Setup: Create 50-100 small shape layers (dots or short lines). Apply this expression to all their Position properties. Each will follow the invisible flow field, creating emergent stream patterns.

// Add to Rotation for particles to align with flow
seed = index * 100;
scale = 0.003;
pos = position;
noiseVal = noise(pos[0] * scale + seed, pos[1] * scale + time * 0.1);
radiansToDegrees(noiseVal * Math.PI * 4);

3. Recursive Feedback Systems

Create self-referential animations where each frame influences the next. Using valueAtTime() to read past states creates organic accumulation—like paint building up or erosion patterns forming.

// Apply to Scale - creates pulsing growth/decay cycles
// Layer references its own past state

feedback = 0.3;               // Influence of past (0-1)
decay = 0.98;                 // Natural decay factor
noiseAmp = 10;                // Random variation

// Get previous frame's scale
prevScale = valueAtTime(time - 0.033);  // One frame back (30fps)

// Apply feedback with noise
variation = noise(time * 2) * noiseAmp;
newScale = prevScale * decay + variation;

// Blend with original
value * (1 - feedback) + [newScale, newScale] * feedback;
// Apply to Position - creates wandering drift
// Particle remembers where it's been

memory = 0.4;                 // How much past affects present
wanderStrength = 20;          // Random walk intensity

// Sample position from 5 frames ago
pastPos = valueAtTime(Math.max(0, time - 0.167));

// Generate new target based on noise
seed = index * 1000;
targetX = pastPos[0] + (noise(time + seed) - 0.5) * wanderStrength;
targetY = pastPos[1] + (noise(time + seed + 100) - 0.5) * wanderStrength;

// Blend current toward target
current = value;
[
    current[0] + (targetX - current[0]) * memory,
    current[1] + (targetY - current[1]) * memory
];

4. Spirograph & Lissajous Patterns

Mathematical harmonics create intricate geometric art. By combining multiple oscillating frequencies, you generate patterns that feel both precise and organic—like celestial mechanics or sound visualized.

// Apply to Position for elegant orbital motion
// Classic Lissajous curves with time evolution

freqX = 3;                    // Horizontal frequency
freqY = 4;                    // Vertical frequency
amplitude = 300;              // Size of pattern
phaseShift = time * 0.2;      // Rotation over time

// Base Lissajous
t = time * 0.5;
x = Math.sin(t * freqX + phaseShift) * amplitude;
y = Math.sin(t * freqY) * amplitude;

// Add secondary harmonic for complexity
x += Math.sin(t * freqX * 2.3) * (amplitude * 0.3);
y += Math.cos(t * freqY * 1.7) * (amplitude * 0.3);

// Center on composition
center = [thisComp.width / 2, thisComp.height / 2];
[center[0] + x, center[1] + y];
// Apply to multiple layers with index-based variation
// Creates spirograph-like layered patterns

layerOffset = index * 0.1;    // Each layer different
baseFreq = 5;
radius = 150 + index * 10;    // Expanding rings

t = time * 0.3;
angle = t * (baseFreq + layerOffset);

// Epitrochoid math (spirograph)
R = 100;
r = 30 + index * 5;
d = 50;

x = (R + r) * Math.cos(angle) - d * Math.cos((R + r) / r * angle);
y = (R + r) * Math.sin(angle) - d * Math.sin((R + r) / r * angle);

center = [thisComp.width / 2, thisComp.height / 2];
[center[0] + x, center[1] + y];
Frequency Ratios for Harmonic Patterns
  • 1:1 - Circle or diagonal line
  • 1:2 - Figure-eight
  • 2:3 - Complex knot pattern
  • 3:4 - Intricate weave
  • 5:7 - Dense, almost chaotic
  • Irrational ratios (π, φ) create never-repeating patterns

5. Emergent Swarm Behavior

Simulate flocking behavior where each particle follows simple rules: avoid crowding, align with neighbors, move toward the center. The result is lifelike swarm motion that emerges from local interactions—no central control.

// Simplified boid-like behavior
// Apply to Position, use with multiple layers

myPos = value;
myIndex = index;
separation = 80;              // Personal space radius
alignStrength = 0.05;         // How much to match neighbors
cohesionStrength = 0.01;      // Pull toward center
maxSpeed = 200;

// Find neighbors (simplified - checks all layers)
avgPos = [0, 0];
avgVel = [0, 0];
separationForce = [0, 0];
count = 0;

for (i = 1; i <= thisComp.numLayers; i++) {
    if (i == myIndex) continue;
    
    otherPos = thisComp.layer(i).position;
    dist = length(myPos - otherPos);
    
    if (dist < 300) {     // Perception radius
        avgPos += otherPos;
        avgVel += thisComp.layer(i).position.velocity || [0,0];
        count++;
        
        // Separation: push away if too close
        if (dist < separation && dist > 0) {
            force = (myPos - otherPos) / dist * (separation - dist);
            separationForce += force;
        }
    }
}

if (count > 0) {
    avgPos /= count;
    avgVel /= count;
    
    // Cohesion: pull toward center of neighbors
    cohesion = (avgPos - myPos) * cohesionStrength;
    
    // Alignment: match average velocity
    alignment = avgVel * alignStrength;
    
    // Apply forces
    velocity = (cohesion + alignment + separationForce * 0.1);
    
    // Limit speed
    if (length(velocity) > maxSpeed * 0.016) {
        velocity = normalize(velocity) * maxSpeed * 0.016;
    }
    
    myPos + velocity;
} else {
    myPos;
}

Note: For performance with many particles, consider using AE's built-in Particle World or third-party plugins like Trapcode Particular. This expression approach works beautifully for 20-50 layers.

6. Reaction-Diffusion Patterns

Simulate chemical reactions that create organic patterns—spots, stripes, and coral-like structures found in nature (zebra stripes, leopard spots, shell patterns). In AE, we approximate this with displacement and feedback.

// Apply to Displacement Map offset or turbulent displace
// Creates evolving organic texture patterns

feed = 0.055;                 // Pattern density (0.01-0.1)
kill = 0.062;                 // Pattern type (0.045-0.07)
scale = 50;                   // Pattern size

// Sample multiple noise octaves
n1 = noise([time * 0.1, 0]);
n2 = noise([time * 0.2 + 100, 0]);
n3 = noise([time * 0.05 + 200, 0]);

// Combine for complexity
pattern = (n1 * feed + n2 * kill + n3 * 0.5) * scale;

// Add subtle animation
pattern + Math.sin(time * 0.5) * 5;

Visual Approach: Apply this to a solid layer with Fractal Noise or Cell Pattern, then use that as a displacement map for your collage layers. The evolving pattern will create organic, living texture shifts.

// Alternative: Direct position displacement for "living" edges
// Apply to Position of a shape layer vertex or mask path

basePos = value;
displaceScale = 20;
freq = 0.5;

// Displacement based on position and time
dx = noise(basePos[0] * 0.01, time * freq) * displaceScale;
dy = noise(basePos[1] * 0.01 + 100, time * freq) * displaceScale;

[basePos[0] + dx, basePos[1] + dy];

7. Audio-Reactive Generative Systems

Connect your generative systems to audio for synesthetic experiences. Sound frequencies drive visual parameters—bass pulses through scale, treble sparkles through rotation, midtones flow through position.

// Apply to Scale - pulses with audio amplitude
// Requires audio layer in composition

audioLayer = thisComp.layer("Audio");  // Name of your audio layer
samplePoint = time;                    // Current time sample
duration = 0.1;                        // Sample window

// Get audio amplitude (both channels averaged)
audioAmp = audioLayer.audioAmplitude;
left = audioAmp.channelLeft;
right = audioAmp.channelRight;
avgAmp = (left + right) / 2;

// Smooth the response
smoothAmp = avgAmp.smooth(0.2, 5);

// Apply to scale with base value
baseScale = 100;
reactivity = 0.5;                      // How much audio affects scale

[baseScale + smoothAmp * reactivity, baseScale + smoothAmp * reactivity];
// Apply to Rotation - responds to frequency bands
// Using Audio Spectrum effect as driver

// Reference Audio Spectrum keyframes or expression
spec = effect("Audio Spectrum")("Audio Spectrum");

// Extract different frequency ranges
bass = spec.valueAtTime(time)[0];      // Low frequencies
mid = spec.valueAtTime(time)[5];       // Mid frequencies
treble = spec.valueAtTime(time)[10];   // High frequencies

// Combine for complex rotation
baseRot = value;
rotSpeed = bass * 0.5 + mid * 0.3;
wobble = Math.sin(time * treble * 0.1) * 10;

baseRot + rotSpeed + wobble;
Audio Setup Tips
  • Add audio → Right-click > Keyframe Assistant > Convert Audio to Keyframes
  • Use pick-whip to link any property to the generated sliders
  • Apply smooth() to reduce jittery response
  • Multiply audio values by small numbers (0.1-2) for subtlety
  • Layer multiple audio-reactive properties for richness

8. Cellular Automata

Grid-based systems where each cell's state depends on its neighbors. Conway's Game of Life is the classic, but variations produce endless pattern types—caves, mazes, coral, cities.

// Simplified cell state using noise as "neighbor influence"
// Apply to Opacity or Scale for grid of shapes

gridX = Math.floor(position[0] / 50);  // Cell column
gridY = Math.floor(position[1] / 50);  // Cell row

// Pseudo-neighborhood using noise
neighbors = noise(gridX * 0.1 + time * 0.2, gridY * 0.1);

// State transition rules
if (neighbors > 0.6) {
    // "Alive" - bright
    100;
} else if (neighbors > 0.3) {
    // "Dying" - fading
    50 + Math.sin(time * 3) * 20;
} else {
    // "Dead" - dim
    10;
}

Setup: Create a grid of small squares using the Repeater (20x20 copies), then apply this expression to Opacity. Each "cell" responds to its virtual neighbors, creating evolving patterns across the grid.

Combining Techniques

The real magic happens when you layer these systems. Try these combinations:

  • Flow field + Audio: Particles follow noise, but audio increases their speed and adds sparkle
  • Lissajous + Feedback: Mathematical orbits that leave decaying trails
  • Fractal + Displacement: Branching structures with living, breathing edges
  • Swarm + Flow field: Flocking behavior influenced by invisible currents
  • Cellular + Reaction-diffusion: Grid patterns that organically dissolve and reform
Workflow for Experimentation
  • Start with one simple expression
  • Add a Control Null with sliders for key parameters
  • Use pick-whip to link expression variables to the sliders
  • Duplicate the layer and vary the Control Null values
  • Add blend modes (Add, Screen, Overlay) for complexity
  • Record slider adjustments for evolving parameters over time

Application in Photo Collage & Motion

For artists working with photography-based collage—static images assembled in Photoshop, then brought into After Effects for subtle animation—these generative techniques serve as invisible glue. They don't replace your collage work; they breathe life into it. Instead of manually keyframing every element, you establish behaviors that evolve organically, creating motion that feels natural rather than mechanical.

Workflow Integration

Photoshop → After Effects Bridge: Export your collage layers as separate PNGs with transparency. In AE, these become the foundation upon which generative motion operates—dust motes drifting across a figure, a cutout flower hovering in dreamlike suspension, or the entire composition pulsing subtly to ambient sound.

Atmospheric Drift

Add generative particles over your collage—dust, snow, or ethereal energy—that move with organic, non-repeating paths. Unlike manually animated particles that loop predictably, noise-driven flow fields create infinite variation.

// Apply to Position of particle layers over your collage
// Creates gentle, organic drift rather than mechanical motion

seed = index * 100;
pos = value;
noiseVal = noise(pos[0] * 0.002 + seed, pos[1] * 0.002 + time * 0.05);
angle = noiseVal * Math.PI * 4;
velocity = [Math.cos(angle) * 0.5, Math.sin(angle) * 0.5]; // Slow, subtle
pos + velocity;

Floating Elements

A cutout figure or object from your collage can hover with meditative, breathing motion. Lissajous curves provide mathematical harmony—movement that feels underwater or in a dream state.

// Apply to Position of a photo cutout
// Slow, meditative floating—no keyframes needed

freqX = 2; freqY = 3;         // Harmonious frequencies
t = time * 0.2;               // Very slow time scale
x = Math.sin(t * freqX) * 15; // Small range—subtle
y = Math.sin(t * freqY) * 10 + Math.cos(t * 5) * 5;

value + [x, y];

Growing Forms

Photo elements of branches, vines, or neural networks can appear to grow or spread organically. Apply fractal branching to a shape layer mask that reveals your photo, or displace the edges of a cutout to make it breathe.

// Apply to a mask path or shape layer revealing your photo
// Angle "breathes" over time for organic movement

baseAngle = 25;
breath = Math.sin(time * 0.3) * 5; // Slow oscillation
baseAngle + breath;

// Combine with fractal branching code from Section 1
// Use the result to animate a mask expansion or trim paths

Audio-Reactive Ambience

Your entire collage can breathe with the soundtrack—imperceptible mathematics, but felt emotionally. Scale, opacity, or position respond to audio amplitude, creating a living relationship between image and sound.

// Apply to Scale or Opacity of your collage layer
// Subtle pulse synchronized to ambient sound

base = 100;
audio = thisComp.layer("Audio").audioAmplitude;
amp = (audio.channelLeft + audio.channelRight) / 2;
// Very subtle reactivity—felt, not obvious
[base + amp * 0.05, base + amp * 0.05];
Practical Tips for Collage Artists
  • Preserve your Photoshop layers—import as composition to maintain editability
  • Use adjustment layers—apply generative effects to nulls or adjustment layers, keeping your photos untouched
  • Pre-compose strategically—group collage elements with their generative motion for organization
  • Render tests at low resolution—expressions can be processor-intensive; test before final renders
  • Save expression presets—build a library of motion behaviors you can apply to future collages

The key insight: these mathematical systems become the atmosphere of your collage—present but not overwhelming, supporting the photographic content rather than competing with it. The motion should feel like a natural property of the image, as if the photograph always contained this latent energy waiting to be released.

Example: Complete Generative Scene

A layered composition combining multiple techniques:

// LAYER 1: Background flow field (50 small dots)
// Position expression - gentle drift
seed = index * 100;
scale = 0.002;
pos = value;
noiseVal = noise(pos[0] * scale + seed, pos[1] * scale + time * 0.05);
angle = noiseVal * Math.PI * 4;
velocity = [Math.cos(angle) * 2, Math.sin(angle) * 2];
pos + velocity;

// LAYER 2: Central Lissajous orbiter
// Position expression - mathematical harmony
freqX = 3; freqY = 4;
t = time * 0.3;
x = Math.sin(t * freqX) * 200 + Math.sin(t * 7) * 50;
y = Math.sin(t * freqY) * 200 + Math.cos(t * 5) * 50;
[thisComp.width/2 + x, thisComp.height/2 + y];

// LAYER 3: Fractal branches (shape layer)
// Path expression with time-evolving angle
angle = 25 + Math.sin(time * 0.2) * 10;  // Breathing angle
// (Use fractal branching code from Section 1)

// LAYER 4: Audio-reactive overlay
// Scale linked to audio amplitude
base = 100;
audio = thisComp.layer("Audio").audioAmplitude;
amp = (audio.channelLeft + audio.channelRight) / 2;
[base + amp * 0.3, base + amp * 0.3];
← Back to AE Tutorials