Chaining and Composition Patterns

hydra-three supports multiple composition styles. These patterns help keep sketches readable and reusable.

Pattern 1: Texture-first material flow

Use Hydra chains to design texture behavior first, then apply to scene objects.

const mat = osc(8, 0.1, 0.8)
  .modulate(noise(2), 0.1)
  .color(0.9, 0.7, 0.8)
  .phong();

scene().lights({ all: true }).mesh(gm.box(), mat).out();

Best for procedural looks and shader-driven motion.

Pattern 2: Scene-first composition

Build geometry/camera/layout first, then swap materials/signals rapidly.

const sc = scene()
  .lights({ all: true })
  .mesh(gm.sphere(), mt.meshPhong())
  .out();

update = () => {
  sc.at(0).rotation.y += 0.01;
};

Best for animation blocking and world design.

Pattern 3: Render-to-texture bridge

Render one chain/scene to texture, reuse it in another scene.

const tex = scene().points([200, 200], mt.dots()).tex(o1);
scene().mesh(gm.plane(2, 2), src(tex).lambert()).out();

Best for layered systems, portals, and feedback experiments.

Pattern 4: Non-global embedding

Prefer namespaced calls in host applications.

const hydra = new Hydra({ makeGlobal: false, detectAudio: false });
const H = hydra.synth;
H.scene().mesh(H.gm.box(), H.osc().phong()).out();

Best for reliability in multi-instance or app-integrated contexts.

Pattern 5: Data-driven textures

Build typed-array data, then map it to textures.

const noiseData = arr.noise(512, 512, { type: "improved" });
const tex = tx.data(noiseData, { min: "linear", mag: "linear" });
scene().mesh(gm.plane(2, 2), src(tex).basic()).out();

Best for custom simulation and algorithmic image sources.

Common pitfalls

  • Avoid mixing many mutable globals in complex patches.
  • Avoid hidden state coupling across outputs unless intentional.
  • Avoid expensive post FX on every output when one composited pass is enough.
  • Avoid using internal methods in production unless you accept API volatility.