Tooling - Shader Modules and Reuse
Why GLSL has no imports, and how glslify turns shader functions into installable modules.
GLSL has no import statement. A shader is a single string of source, compiled as a unit, with no way to reference code in another file.
So shaders grow by copy-paste. A noise function pasted in from a blog post gets pasted into the next project, and the one after, drifting slightly each time.
Where This Fits
The tooling layer — a build step alongside your shaders, not a feature of GLSL or of any engine. Optional; the argument for adopting it is about a body of work rather than a single piece.
Assumes Shaders and the GPU. Most useful once you reach Waves and Noise.
glslify
glslify runs over shader source before compilation, resolves require statements against npm, and produces a single flat string with everything inlined — which is the only thing the GPU accepts. The shader still ends up as one string; you just did not write it that way.
Because the modules come from npm they are versioned and installable, so glsl-noise is a line in package.json rather than three hundred lines pasted into a fragment shader.
Imports use a #pragma directive, C’s escape hatch for compiler-specific instructions and therefore something GLSL tolerates:
#pragma glslify: noise = require('glsl-noise/simplex/2d')
float fbm (vec2 p, int octaves) {
float sum = 0.0;
float amplitude = 0.5;
float frequency = 1.0;
for (int i = 0; i < 8; i++) {
if (i >= octaves) break;
sum += amplitude * noise(p * frequency);
frequency *= 2.0;
amplitude *= 0.5;
}
return sum;
}

One #pragma line, and simplex noise is available as an ordinary function. Nothing about the implementation appears in the file.
The name on the left is what the function is called locally, so it can be whatever reads best. The path on the right is either an npm module or a relative file.
A tool consuming glslify needs to be told which strings are shaders — wrapping the source in a glslify() call is the usual signal. Shaders living in their own .glsl, .vert or .frag files are handled automatically, and past a certain length that is the better arrangement anyway.
Writing a Module
A glslify module exports exactly one thing:
vec3 red () {
return vec3(1.0, 0.0, 0.0);
}
#pragma glslify: export(red);
#pragma glslify: red = require('./red.glsl');
void main () {
vec3 r = red();
}
The one-export rule is worth understanding rather than working around. Because the importer names the function at the call site, a module exporting several things would need a convention to disambiguate them, and that convention leaks into every importing shader. One function per file keeps the namespace under the caller’s control, which matters in a language with almost no scoping.
Grouping related functions therefore means several small files. That is the intended shape.
What Is Worth Installing
| Module | Provides |
|---|---|
glsl-noise | Simplex and Perlin noise in 2D, 3D and 4D |
glsl-random | A standard pseudo-random function |
glsl-aastep | An anti-aliased step(), for edges without jaggies |
glsl-hsl2rgb | HSL to RGB conversion, for palette work |
glsl-dither | Ordered dithering patterns |
glsl-aastep is worth knowing for a non-obvious reason. A hard step() edge is a jagged staircase, because each pixel is fully inside or outside. An anti-aliased step uses the rate of change across neighbouring pixels to produce a one-pixel ramp instead — it depends on a hardware feature that has to be enabled on the material, so it is not purely a shader-side change.
The Tradeoff
A shader written against glslify does not compile on its own, so pasting it into a live editor to debug means resolving the imports by hand first.
For a single small shader that is a poor trade. For a body of work where the same noise, easing and colour functions recur, it is the difference between a library and a pile of duplicated source.
See Also
- Waves and Noise — the functions most worth importing rather than writing
- canvas-sketch Workflow — a harness with glslify already wired in
- Shaders and the GPU — the language these modules are written in
Resources
- glslify — the module system and its pragma syntax
- glsl-noise
- stack.gl packages — the wider ecosystem these came out of
Source: WebGL & GLSL — A Primer by Matt DesLauriers, and its glslify and modules guides