Onceworlds
Engine · Materials

Material looks and shaders

Looks in one line (rim, dissolve, hologram, wind...) and node-graph shaders.

Most looks a game wants are one line; the API reference ("Material looks") has every field.

The default path: a look in one line

import { Transform } from '@onceworlds/engine';
import { Material, Mesh } from '@onceworlds/engine/modules';

world.spawn([Transform(), ...Mesh.sphere(0.5, Material.pulse('#ffd23f'))]);            // a pickup that glows and breathes
world.spawn([Transform(), ...Mesh.box(1, Material.rim('#2d6cdf', { kind: 'toon' }))]);  // a cartoon hero with a bright edge
Look For
Material.rim(color) Heroes, selected or highlighted things, ghosts: a glow at the edges in the emissive colour (white unless you set it).
Material.dissolve(color) Spawning in and dying out: tween opacity from 1 to 0 and it burns away with a glowing edge (emissive). It stays solid.
Material.hologram(color) Holograms, force fields, ghosts, UI in the world: see-through scan lines that flicker.
Material.triplanar(color, { map }) Rocks, cliffs, walls, ground made of boxes: the texture is laid from the world, no stretching, no UVs.
Material.foliage(color) Leaves, grass, bushes, flags: sways in the wind, more at the top.
Material.pulse(color) Pickups, buttons, warnings, power-ups: the glow breathes.

Each mixes with kind: 'toon' (cartoon bands) or 'unlit', and keeps instancing: a thousand coins with the same look are one draw. effectStrength and effectSpeed (1 is the look's own) make it more or less and faster or slower.

Per entity, change only color, opacity, emissive and emissiveIntensity. Those are each entity's own and cost nothing to animate (a rim that turns red when hit: set emissive). Any other field makes a different material.

Your own shading: Material.shader

When no look fits (lava rock, a force field with your own pattern, a pulsing crystal), write a node graph. It is lit by the scene's sun, lights and shadows like a standard material (or lit: 'toon' / 'unlit'), and runs on WebGPU and on WebGL2 phones alike.

import { t, Transform } from '@onceworlds/engine';
import { Material, Mesh } from '@onceworlds/engine/modules';

const Lava = Material.shader({
  uniforms: { speed: t.f32(1), hot: t.color('#ff6a1a') },
  build: ({ TSL, u, position, time }) => {
    const n = TSL.mx_noise_float(position.mul(0.9).add(TSL.vec3(0, time.mul(u.speed), 0))).mul(0.5).add(0.5);
    return { color: TSL.vec3(0.08, 0.05, 0.04), emissive: u.hot.rgb.mul(n.pow(3).mul(6)) };
  },
});
world.spawn([Transform(), ...Mesh.sphere(1, Lava({ speed: 2 }))]);

Mistakes

Don't Do
Material.toon('#4fb36b', { effect: 'rim' }) (no emissive colour: the rim is black) Material.rim('#4fb36b', { kind: 'toon' })
Fade a Material.dissolve with transparent: true Leave it solid: opacity is how much is left
Set a shader uniform in a system every frame Use time in build, or color / emissive per entity
Lava({ sped: 2 }) The error lists the uniforms the shader has and how to declare one
Material.hologram on a big wall the camera sits behind Holograms are see-through and drawn after the rest: keep them small and few