Skip to content

Author a Custom Material

Write your own material by dropping small snippets of WGSL into the engine's shading pipeline - a wind-sway vertex, a tinted surface colour, a perturbed normal, or a cutout mask - and assign it to an object.

Awaken materials are hook-based. Rather than writing a whole shader, you fill in up to four well-defined hook slots and the engine composes them into its standard lit shader (composeShader). Each slot is a short piece of WGSL that runs at a specific point in the pipeline, and you expose tunable values as params the hooks read as params.<name>. This tutorial builds one custom material from a fresh asset and assigns it, using the Shader Editor.

Coming soon

This tutorial is being written. The step outline and hook reference below are final, so you can author a material today. Finished prose, screenshots, and a copy-paste sample are on the way.

What you will build

A custom material - for example a gently swaying, tinted surface - authored from WGSL hooks with a couple of Inspector-editable params, then assigned to an object in your scene.

The four hook slots

The Shader Editor exposes exactly four slots, in pipeline order. Leave a slot blank and its marker is simply stripped - you pay for only what you use.

The table below is in execution order - the order the GPU runs them, and the order the editor lists them in.

SlotRuns atFill it to…
Vertex Positionvertex stage, after the model transform (before the clip transform)modify world - the world-space vertex position (a vec4<f32>) - e.g. wind sway
Discardfirst statement of the fragment stageearly-exit a fragment: if (…) { discard; } using in.uv / in.worldPos / params.*
Surface Normalfragment stage, before shadingperturb N - the mutable world-space normal - detail, waviness
Surface Colorfragment stage, before lightingset or adjust albedo (a mutable vec3<f32>, rgb) and outAlpha - tints, gradients, transparency

Discard runs before the albedo texture is sampled, so texel is not in scope there - it can't do texture alpha-testing. It doesn't need to: the template already discards at texel.a < 0.5 for every material. Use this slot for cutouts the engine can't guess - UV patterns, world-height clipping, param-driven dissolves.

Params can be float, vec2, vec3, vec4, color, bool, or texture, and every hook reads them as params.<name>. A few engine values are in scope too: globals.time (seconds, handy for animating sway) and globals.anim (0 while editing, 1 during Play - multiply your time by it to freeze animation in the editor), plus - in the fragment hooks - the interpolated varyings on in (in.worldPos, in.uv, in.color).

Hooks or a node graph

The Shader Editor has two create buttons. + New Shader authors the hook slots this tutorial uses; + New Shader Graph opens a node canvas that compiles to those same hooks and params. On a graph, the Tidy button in the Node Graph header auto-arranges the nodes left→right with the Surface node on the right (undoable), and the Fit button beside it frames the whole graph. This tutorial fills the hook slots directly.

Before you start

Steps

  1. Create the material. Open the Shader Editor and click + New Shader. Rename it something memorable inline.
  2. Add params. Add the values your hooks will read - for a sway material, a float swaySpeed, a float swayAmount, and a color tint. Each becomes an editable row; hooks reference them as params.swaySpeed, params.tint, and so on.
  3. Fill the hook slots. Write WGSL into the slots you need. For example, offset world in Vertex Position for sway (world.x = world.x + sin(globals.time * params.swaySpeed + world.z) * params.swayAmount;) and tint in Surface Color with albedo = albedo * params.tint;. Edits mirror live - the Viewport re-renders as you type. You don't have to guess whether the WGSL is valid: the editor compiles it for real as you type and shows any error as a squiggle on the offending line, with a ✓ compiles / ✗ N errors status beside the material name. If you do render a broken hook, the object falls back to the default shader rather than breaking the frame.
  4. Assign it. Select an object, and in the Inspector set its material to your new asset (or drag the material onto the object). See Materials for how the assignment is stored.
  5. Tune. Adjust the params and watch the surface update. A param marked per-object can also be overridden on each entity from its MeshMaterial component in the Inspector - so one material can dress many objects with different colours or speeds, without duplicating it. Resetting an override clears it, so that object goes back to following the material's own default.
  6. Play and ship. The composed shader runs identically in Play and in the exported game - see Render Pipeline Overview and Export game.html.

📸 Screenshot - save as img/tutorials-custom-shader.png

The Shader Editor: the four hook slots (one filled with a WGSL snippet), the param table below them, and the Viewport re-rendering the assigned object live.

Where to go next

See also

Awaken — browser-native WebGPU game engine.