Back to Houdini Work

Pipe Dream Houdini

Feb. 24, 2020

Snow falling across procedural pipes
Snow particles settling on procedural pipe geometry.

For this project, I set out to recreate the classic Microsoft Windows 3D Pipes screensaver that captivated so many of us in the 1990s. Studying footage of the original screensaver, I mapped out the foundational rules governing pipe generation:

  1. Pipes initialize from a single spherical joint and a starter segment. Each frame, a pipe extends by exactly one segment.
  2. Starting positions, hues, directional paths, and camera tumble are randomized.
  3. Pipes must never collide or intersect with themselves or other existing pipes.
  4. Pipes are confined within a visible three-dimensional boundary volume.

My first attempt utilized an L-system with turtle commands: each iteration stepped forward with equal probability of continuing straight or banking along cardinal axes. However, this came with two critical roadblocks: the grammar had no spatial memory to detect impending self-intersections, and constraining growth strictly within a volumetric bounding box proved difficult.

First L-system pipe attempt with intersections
Early test utilizing a Lindenmayer System. Notice the overlapping lines and intersections near the bottom right.

The VEX Point Array System

I rearchitected the generator using a VEX point-array system, leveraging Python to handle scene seeds and high-level attributes. In this setup, each pipe claims an unoccupied grid node and queries adjacent coordinate neighbors before choosing its next step:

Point grid interconnection test
Early test when points connected to all adjacent lattice neighbors rather than a single trajectory.
First pipe without self-intersections
First successful single pipe generation without any self-intersections.
Multi-pipe collision conflict test
Initial two-pipe test encountering point-occupancy conflicts when claiming adjacent space.
Multi-pipe coordination without collisions
Successful spatial arbitration across multiple concurrent pipes.
Curve generation workflow: points connect, smooth, and sweep into geometry via the PolyWire SOP while elbow spheres are dynamically placed at 90-degree turns.
Procedural animation cycle with six concurrent pipes weaving through space.
Pipes with joint spheres on corners
Six pipes with joint spheres assigned to random corners, recreating the iconic screensaver look.
Rendered pipes in boundary volume
Final look development render. Pipe count, frame thresholds, boundary dimensions, radius, joint spheres, materials, and custom collision guides are exposed as HDA parameters.
Pipes rendered with a tinted glass material, routing randomized point colors directly into the shader transmission channel.
Clear glass shader constrained to a single 2D plane with reduced resample count to produce squiggly trajectories.
Testing HDA resample count parameters Testing HDA boundary volume parameters
Exploring procedural variations: tuning curve subdivision density and adjusting volumetric bounding boxes.
Pipes growing with the curve resample count set to 1.
Sampling image texture maps to project color across the pipe network while dynamically modulating segment radii.

Scaling to Arbitrary Pipe Counts

In my original prototype, I used a Foreach loop iterating over six distinct VEX scripts per frame. This approach was computationally heavy and fundamentally hardcoded to a maximum of six pipes. Decreasing pipe counts simply deleted pipes post-simulation rather than dynamically redistributing spatial paths.

Researching solver optimization led me to Entagma's excellent post on VEX space-filling curves. Inspired by their workflow, I transitioned from duplicating static 3D point grids to dynamically sourcing seed points from VDB volumes, then running the simulation inside a native Solver SOP.

Unlimited pipes architecture render
Refactored solver architecture capable of simulating dozens of concurrent pipes seamlessly.

Within the solver, each pipe receives an integer identifier (@pipe_n) and calculates unique pseudo-random seed colors. To prevent race conditions where two pipes claim the exact same open coordinate on the same frame, an inner Foreach loop evaluates each pipe sequentially per sub-step. Points are flagged as occupied immediately as a segment is committed, guaranteeing that two pipes can never intersect—even when running dozens of concurrent paths.

Pipes procedurally populating and conforming to the internal volume of the Houdini "Tommy" test asset.