Chaining and Composition Patterns

triode 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();

stage().lights({ all: true }).mesh(geom.box(), mat).render();

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 = stage()
  .lights({ all: true })
  .mesh(geom.sphere(), mat.meshPhong())
  .render();

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 pointTex = stage().points([200, 200], mat.dots()).tex(o1);
stage().mesh(geom.plane(2, 2), src(pointTex).lambert()).render();

Best for layered systems, portals, and feedback experiments.

Pattern 4: Non-global embedding

Prefer namespaced calls in host applications.

const triode = new Triode({ makeGlobal: false, detectAudio: false });
const H = triode.synth;
H.stage().mesh(H.geom.box(), H.osc().phong()).render();

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 dataTex = tex.data(noiseData, { min: "linear", mag: "linear" });
stage().mesh(geom.plane(2, 2), src(dataTex).basic()).render();

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.