Install
Same three ways as every jen pack. The pack only writes text files: no build step, no runtime, nothing to link.
Globally
$ npm install -g @codejen/jen @codejen/pack-glsl $ jen glsl:webgl --name=Clouds
Locally
$ npm install --save-dev @codejen/jen @codejen/pack-glsl $ npx jen glsl:webgl --name=Clouds
Not at all
Ask for a glsl: generator without the pack installed and jen fetches @codejen/pack-glsl into a throwaway directory, runs it once and removes it again (first-party @codejen scope only; JEN_NO_FETCH=1 turns it off).
$ npx @codejen/jen glsl:webgl --name=Clouds Fetching @codejen/pack-glsl@latest …
Where files go
Generators write into the folder you run them in, or --dir: from the project root, glsl:frag --name=Clouds --dir=shaders writes shaders/clouds.frag.glsl. --into and --file are relative to the current directory. glsl:webgl makes a new project folder in the current directory instead, with the shader in shaders/ inside it (--shaderDir renames that). The home page has the general rules. jen glsl:noise --help lists a generator's params in the terminal.
Starters
Shaders default to WebGL2's GLSL ES 3.00, so you can prototype in the browser first. Everything compiles as desktop GLSL 3.30 and 4.10 core too: pass --version=410, or switch an existing shader with glsl:port. The starters use the uniforms every jen host feeds – uTime, uResolution, uMouse – so the same file runs in <shader-canvas> and in cpp:sdl3-opengl.
glsl:webgl
A playground without a build step: an index.html, the <shader-canvas> element and a fragment shader it draws full-screen, recompiling whenever you save. Every preview on this page is a <shader-canvas>.
$ jen glsl:webgl --name=Clouds glsl:webgl (pack @codejen/pack-glsl) – create a no-build WebGL2 playground + index.html + shader-canvas.js + shaders/clouds.frag.glsl ✓ 3 written $ npx serve
shaders/clouds.frag.glsl, as generatedParams
name
page title and shader name: Clouds makes shaders/clouds.frag.glsl
dir
folder for the playground, relative to the current directory
shaderDir
folder for the shader inside it
tag
custom element name, if shader-canvas clashes with something on the page
inline
put the shader into the page, so it opens from disk without a web server
glsl:frag
A full-screen fragment shader: centered, aspect-correct coordinates from gl_FragCoord, and the jen uniforms.
$ jen glsl:frag --name=Plasma --dir=shaders glsl:frag (pack @codejen/pack-glsl) – create a full-screen GLSL fragment shader starter + shaders/plasma.frag.glsl ✓ 1 written
Params
name
file name: Clouds makes clouds.frag.glsl
version
GLSL dialect: 300es (WebGL2), 330 or 410 (desktop OpenGL core)
glsl:vert
A vertex shader: --kind=quad (default) takes a full-screen quad at attribute location 0 like cpp:sdl3-opengl, triangle covers the screen from gl_VertexID without any buffer, and mesh has position, normal and UV attributes with uModel/uView/uProjection.
$ jen glsl:vert --name=Mesh --kind=mesh --dir=shaders glsl:vert (pack @codejen/pack-glsl) – create a GLSL vertex shader starter + shaders/mesh.vert.glsl ✓ 1 written
Params
name
file name: Clouds makes clouds.vert.glsl
kind
quad (full-screen, matches cpp:sdl3-opengl), triangle (full-screen, no vertex buffer) or mesh (3D model with normals for lighting)
version
GLSL dialect: 300es (WebGL2), 330 or 410 (desktop OpenGL core)
glsl:port
Switches a shader between dialects by rewriting only its header: the #version line and the default precisions. For moving a WebGL prototype into cpp:sdl3-opengl, and back. To compile such a shader into a C++ build, cpp:shader --file=shaders/clouds.frag.glsl from the cpp pack embeds it as a C string.
$ jen glsl:port --file=shaders/clouds.frag.glsl --to=410 glsl:port (pack @codejen/pack-glsl) – switch a shader between GLSL dialects by rewriting its header ~ shaders/clouds.frag.glsl /^[ \t]*precision\s+(?:lowp|mediump|highp)\s+(?:float|int)\s*;[ \t]*\r?\n/gm replaced ~ shaders/clouds.frag.glsl /^#version[^\n]*$/m replaced ✓ 1 written
Params
file
the shader to convert
to
target dialect: 300es (WebGL2), 330 or 410 (desktop OpenGL core)
Functions
How they get into your shader
GLSL has no #include, so the pack ships its functions as plain, attributed .glsl files. With --into=<shader>, jen inserts the ones you pick, plus everything they need, above a // jen:functions marker. If the shader has none yet, the marker goes in before its first function, so your own code below can call them. Anything already in the file is skipped: run a generator twice, or two generators sharing a helper, and nothing is duplicated.
Without --into, every function becomes its own file under lib/ where the files go (shaders/lib/ with --dir=shaders), for setups with an include mechanism (vite-plugin-glsl, glslify, three.js). Most generators also take --name for a ready-to-run preview shader.
glsl:noise
Value, Perlin, simplex and Worley noise; fBm, turbulence, ridged and domain-warped fractals on any of them; curl noise for flow fields. In 2D and 3D, and every function has a period overload that tiles seamlessly – SVG feTurbulence's stitchTiles, for any noise. The previews below are tiled 2×2: a seam would show as a cross in the middle.
$ jen glsl:noise --kind=fbm --into=shaders/clouds.frag.glsl glsl:noise (pack @codejen/pack-glsl) – add GLSL noise, tileable ~ shaders/clouds.frag.glsl + // jen:functions – jen glsl:* adds functions above this line ~ shaders/clouds.frag.glsl + // psrdnoise (c) 2021 Stefan Gustavson and Ian McEwan (+125 more) ~ shaders/clouds.frag.glsl + // simplexNoise (2D) · pack-glsl · MIT-0 (+14 more) ~ shaders/clouds.frag.glsl + // simplexFbm (2D) · pack-glsl · MIT-0 (+42 more) ✓ 1 written
--kind=value--kind=perlin--kind=simplex--kind=worley--kind=fbm--kind=turbulence--kind=ridged--kind=warp--kind=curl: flow direction as color$ jen glsl:noise --kind=warp --base=perlin --name=Marble --tile --dir=shaders glsl:noise (pack @codejen/pack-glsl) – add GLSL noise, tileable + shaders/marble.frag.glsl ~ shaders/marble.frag.glsl + // jen:functions – jen glsl:* adds functions above this line ~ shaders/marble.frag.glsl + // webgl-noise (https://github.com/stegu/webgl-noise) (+22 more) … ~ shaders/marble.frag.glsl + // perlinFbm (2D) · pack-glsl · MIT-0 (+42 more) ~ shaders/marble.frag.glsl + // perlinWarp (2D) · pack-glsl · MIT-0 (+21 more) ✓ 1 written
Fractals are written once and renamed for the base you pick – simplexFbm, perlinFbm – so several can share a shader. They take an optional gain for roughness; warp takes a strength instead.
Params
kind
pick one: value, perlin, simplex, worley; fractals fbm, turbulence, ridged, warp; or curl (flow)
dim
2 or 3 dimensions
base
for fractals: the noise they are built from – value, perlin, simplex or worley
into
existing shader to insert the functions into, before main() (default: one file each in lib/)
name
instead of --into: create a preview shader with this name that shows the noise
tile
with --name: draw the tileable noise 2×2 times, to show it is seamless
version
GLSL dialect: 300es (WebGL2), 330 or 410 (desktop OpenGL core)
glsl:sdf
Signed distance functions: shapes, operators to combine them, and – in 2D – antialiased masks for fill, stroke, glow and shadows. Pick with --shapes, --ops and --effects, each also takes all.
$ jen glsl:sdf --dim=3 --shapes=sphere,torus --ops=smooth-union --into=shaders/scene.frag.glsl glsl:sdf (pack @codejen/pack-glsl) – add signed distance functions = shaders/scene.frag.glsl already present: // jen:functions – jen glsl:* adds functions above this line ~ shaders/scene.frag.glsl + // sdSphere · pack-glsl · MIT-0 (+4 more) ~ shaders/scene.frag.glsl + // sdTorus · pack-glsl · MIT-0 (+7 more) ~ shaders/scene.frag.glsl + // opSmoothUnion · pack-glsl · MIT-0 (+8 more) ✓ 1 written
--shapes=all --name=…: circle, box, round-box, segment, polygon, star--dim=3 --shapes=all --name=…: sphere, box, round-box, torus, capsule, cylinderWhat to pick
shapes
circle, box, round-box, segment, polygon, star
shapes
sphere, box, round-box, torus, capsule, cylinder, plane
ops
union, subtract, intersect, their smooth- variants, round, onion, repeat, repeat-limited, extrude, revolve
effects
fill, stroke, glow, shadow, inner-shadow
Params
dim
2 (shapes to draw) or 3 (solids to raymarch)
shapes
comma-separated or all – 2D: circle, box, round-box, segment, polygon, star; 3D: sphere, box, round-box, torus, capsule, cylinder, plane (pick at least one of --shapes, --ops, --effects)
ops
comma-separated or all: union, subtract, intersect, smooth-union/-subtract/-intersect, round, onion, repeat, repeat-limited, extrude, revolve (pick at least one of --shapes, --ops, --effects)
effects
2D only, comma-separated or all: fill, stroke, glow, shadow, inner-shadow (pick at least one of --shapes, --ops, --effects)
into
existing shader to insert the functions into, before main() (default: one file each in lib/)
name
instead of --into: create a preview shader with this name, the shapes side by side
version
GLSL dialect: 300es (WebGL2), 330 or 410 (desktop OpenGL core)
glsl:raymarch
A raymarching starter: a scene() distance function, an orbiting camera, soft shadows, ambient occlusion and fog, lit with any model from glsl:lighting. --materials gives every surface a material id; --minimal leaves out shadows, AO and fog. With --into, you get just the helpers – raymarch, calcNormal, softShadow, calcAO, cameraMatrix – for your own float scene(vec3 p).
$ jen glsl:raymarch --name=Scene --lighting=pbr --materials --dir=shaders glsl:raymarch (pack @codejen/pack-glsl) – raymarch signed distance fields + shaders/scene.frag.glsl ~ shaders/scene.frag.glsl + // jen:functions – jen glsl:* adds functions above this line ~ shaders/scene.frag.glsl + // sdPlane · pack-glsl · MIT-0 (+5 more) … ~ shaders/scene.frag.glsl + // raymarch · pack-glsl · MIT-0 (+18 more) ~ shaders/scene.frag.glsl + // calcNormal · pack-glsl · MIT-0 (+15 more) … ~ shaders/scene.frag.glsl + // BakingLab (https://github.com/TheRealMJP/BakingLab) (+23 more) ~ shaders/scene.frag.glsl + // tonemapAces: Stephen Hill's fit of the ACES filmic curve (RRT + ODT), via (+34 more) ~ shaders/scene.frag.glsl + // linearToSrgb · pack-glsl · MIT-0 (+15 more) ✓ 1 written
--lighting=lambert--lighting=toon--lighting=pbr --materialsParams
name
create a starter shader with this name: a small scene, orbiting camera, shadows, AO and fog
minimal
with --name: leave out soft shadows, ambient occlusion and fog
materials
with --name: a material id per surface, for different colors
lighting
with --name: lambert, blinn, toon or pbr (see glsl:lighting)
parts
with --into, comma-separated: march, normal, soft-shadow, ao, camera – or all
into
existing shader to add the raymarching helpers to, for your own scene()
version
GLSL dialect: 300es (WebGL2), 330 or 410 (desktop OpenGL core)
glsl:lighting
Shading models with one signature – normal, view and light direction, then the material – so you can swap them: lambert, blinnPhong, toon with a rim light, and pbr (Cook-Torrance: GGX, Smith-Schlick, Schlick Fresnel, metallic/roughness) with an ambient light that needs no environment map. Plus color output: ACES and Reinhard tonemapping, and the exact sRGB curve.
$ jen glsl:lighting --models=toon --tonemap=srgb --into=shaders/clouds.frag.glsl glsl:lighting (pack @codejen/pack-glsl) – add lighting and color output = shaders/clouds.frag.glsl already present: // jen:functions – jen glsl:* adds functions above this line ~ shaders/clouds.frag.glsl + // toon · pack-glsl · MIT-0 (+17 more) ~ shaders/clouds.frag.glsl + // linearToSrgb · pack-glsl · MIT-0 (+15 more) ✓ 1 written
Params
models
comma-separated shading models: lambert, blinn, toon, pbr – or all (pick at least one of --models, --tonemap)
tonemap
comma-separated color output: aces, reinhard (HDR to [0, 1]), srgb (display encoding) – or all (pick at least one of --models, --tonemap)
into
existing shader to insert the functions into, before main() (default: one file each in lib/)
glsl:displace
Moves surfaces in the vertex shader and keeps the normals right, so the lighting follows: displace() pushes vertices along their normal by your displacement(p), gerstnerWave() sums up ocean waves with exact normals. --name writes a vertex/fragment pair for <shader-canvas mesh="sphere"> – drag the previews to turn them.
$ jen glsl:displace --kind=noise --name=Blob --dir=shaders glsl:displace (pack @codejen/pack-glsl) – displace surfaces in the vertex shader with correct normals + shaders/blob.vert.glsl ~ shaders/blob.vert.glsl + // jen:functions – jen glsl:* adds functions above this line ~ shaders/blob.vert.glsl + // orthonormalBasis · pack-glsl · MIT-0 (+11 more) ~ shaders/blob.vert.glsl + // displace · pack-glsl · MIT-0 (+24 more) … + shaders/blob.frag.glsl ~ shaders/blob.frag.glsl + // jen:functions – jen glsl:* adds functions above this line ~ shaders/blob.frag.glsl + // blinnPhong · pack-glsl · MIT-0 (+14 more) ~ shaders/blob.frag.glsl + // linearToSrgb · pack-glsl · MIT-0 (+15 more) ✓ 2 written
--kind=noise on mesh="sphere"--kind=waves on mesh="plane"Params
kind
noise (3D simplex along the normal), wobble (sine) or waves (Gerstner waves on a flat surface)
into
existing vertex shader to insert the functions into (default: one file each in lib/)
name
instead of --into: create a preview vertex + fragment shader pair with this name
version
GLSL dialect: 300es (WebGL2), 330 or 410 (desktop OpenGL core)
glsl:matrix
Matrices for GPU-side transforms, following OpenGL conventions: rotateX/Y/Z and rotateAxis, perspective, lookAt, translate, scale, and transform to lift a mat3 into a mat4.
$ jen glsl:matrix --fns=rotate --into=shaders/mesh.vert.glsl glsl:matrix (pack @codejen/pack-glsl) – add matrix helpers ~ shaders/mesh.vert.glsl + // jen:functions – jen glsl:* adds functions above this line ~ shaders/mesh.vert.glsl + // rotateX, rotateY, rotateZ, rotateAxis · pack-glsl · MIT-0 (+32 more) ✓ 1 written
Params
fns
comma-separated: rotate, perspective, look-at, transform (also as translate or scale) – or all
into
existing shader to insert the functions into, before main() (default: one file each in lib/)
glsl:util
Small helpers the other functions build on: PI/TAU, rot2, remap, saturate, and integer hashes for lattice cells.
$ jen glsl:util --fns=rot2,remap --into=shaders/clouds.frag.glsl glsl:util (pack @codejen/pack-glsl) – add GLSL helpers = shaders/clouds.frag.glsl already present: // jen:functions – jen glsl:* adds functions above this line ~ shaders/clouds.frag.glsl + // rot2 · pack-glsl · MIT-0 (+7 more) ~ shaders/clouds.frag.glsl + // remap · pack-glsl · MIT-0 (+5 more) ✓ 1 written
Params
fns
comma-separated helpers: consts (PI, TAU), rot2, remap, saturate, hash – or all
into
existing shader to insert the functions into, before main() (default: one file each in lib/)
Licenses and credits
The pack's own code is MIT-0: use it however you like, no attribution required. Code by others is only included under the MIT license, and when jen inserts it into your shader, the author's notice goes in with it – once per shader. Formulas the pack reimplements are credited in each function.
- webgl-noise by Stefan Gustavson and Ashima Arts, MIT: classic Perlin noise.
- psrdnoise by Stefan Gustavson and Ian McEwan, MIT: tiling simplex noise and its gradients, behind
simplexNoiseandcurlNoise. - BakingLab by MJP and David Neubelt, MIT: Stephen Hill's ACES fit,
tonemapAces. - hash-prospector by Chris Wellons, public domain: the
lowbias32hash constants. - Formulas from Inigo Quilez's articles on distance functions, smooth minimum, normals and soft shadows; Brian Karis's "Real Shading in Unreal Engine 4"; Duff et al.'s orthonormal basis (JCGT 2017); Mark Finch's Gerstner waves (GPU Gems).
The full notices: THIRD_PARTY_NOTICES.md.