---
title: "Shader module (asset type)"
description: "A .shaderModule is a block of WGSL that other shaders include rather than copy — a lighting model, a set of colour-space helpers, one package's shared prelude."
section: "Types"
slug: "types-shadermodule"
canonical: "https://origozero.ai/docs/types-shadermodule"
updated: "2026-10-13T20:37:09.334725121+00:00"
tags: ["asset-type", "reference"]
---

# Shader module (asset type)

## Folder shape

```
<name>.shaderModule/
  module.wgsl        # the WGSL — helpers, structs, constants (required)
  README.md          # what THIS module provides (required)
```

Each file carries a committed `.meta` sidecar pinning its stable guid, and the
folder carries one of its own.

## Including one

A shader names a module by identity, and the identity is what records the
dependency — so a shader that is packed, pulled or published carries the modules
it includes with it:

```wgsl
#include "~.colour"                         // a module in the same package
#include "@builtin::shaderModules.pbr_shading"      // one from the builtin library
```

A missing module is a hard error at compile: a shader that names an interface
which no longer exists fails loudly rather than compiling against a stale copy.
Each module is expanded at most once per shader, so two shaders that both
include the same module — or a module that includes another — produce one copy,
not a duplicate-definition error.

## Writing one

Write only what other shaders need in scope: functions, structs, constants. A
module may reference a binding its includers declare, which is how a package's
prelude can read that package's uniform block.

```wgsl
const LUMA: vec3<f32> = vec3<f32>(0.2126, 0.7152, 0.0722);

fn srgb_to_linear(c: vec3<f32>) -> vec3<f32> {
    let lo = c / 12.92;
    let hi = pow(max(c + vec3<f32>(0.055), vec3<f32>(0.0)) / 1.055, vec3<f32>(2.4));
    return select(hi, lo, c <= vec3<f32>(0.04045));
}
```

## Answering to another name

A module declares extra names it answers to with `@alias` lines in the header of
`module.wgsl`:

```wgsl
// @alias: colour_helpers
// @alias: @mypack::shaderModules.colour
```

Each becomes an alias on the asset, so the name resolves everywhere an identity
does — `asset.resolve`, an `#include`, and the publish gate alike. That is what
lets a module be renamed without breaking the shaders already written against
its old name: content persists the source it was authored with, so a name that
stops resolving is a world that stops publishing.

Only the header is read — the scan stops at the first line that is not a
comment, so a directive cannot hide in the module body. A name another asset
already holds is refused and reported, since an alias extends the identity
namespace rather than taking a name out of another asset's hands.

## Registration & hot reload

Writing `module.wgsl` registers the module under every name the asset answers to
— its guid, its identity, and any alias — so a shader includes it by whichever
name it holds. Saving re-registers it and the shaders that include it recompile,
with no world reload.

## Discovery

- `asset.list("shaderModule")` — every registered module.
- `moduleRef:getSource()` — the module's WGSL.

## Related types

- `.shader` — render-domain shaders (surface, sky, post-process, screen).
- `.computeShader` — compute programs.

## In the Inspector

Selecting a shader module opens its **Module** section: how many lines it holds, the aliases a shader `#include`s it by, and an opener for `module.wgsl`.
