@codejen/pack-glsl

shaders.

Every generator, with the command, the output it prints, and the shader it makes – running live.

GLSL starters and functions for WebGL2 and OpenGL. MIT-0, attributed where it's someone else's.

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 generated

Params

name

page title and shader name: Clouds makes shaders/clouds.frag.glsl

required

dir

folder for the playground, relative to the current directory

default .

shaderDir

folder for the shader inside it

default shaders

tag

custom element name, if shader-canvas clashes with something on the page

default shader-canvas

inline

put the shader into the page, so it opens from disk without a web server

default false

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

required

version

GLSL dialect: 300es (WebGL2), 330 or 410 (desktop OpenGL core)

default 300es

one of 300es, 330, 410

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

required

kind

quad (full-screen, matches cpp:sdl3-opengl), triangle (full-screen, no vertex buffer) or mesh (3D model with normals for lighting)

default quad

one of quad, triangle, mesh

version

GLSL dialect: 300es (WebGL2), 330 or 410 (desktop OpenGL core)

default 300es

one of 300es, 330, 410

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

required

to

target dialect: 300es (WebGL2), 330 or 410 (desktop OpenGL core)

required

one of 300es, 330, 410

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)

required

one of value, perlin, simplex, worley, fbm, turbulence, ridged, warp, curl

dim

2 or 3 dimensions

default 2

one of 2, 3

base

for fractals: the noise they are built from – value, perlin, simplex or worley

default simplex

one of value, perlin, simplex, 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

default false

version

GLSL dialect: 300es (WebGL2), 330 or 410 (desktop OpenGL core)

default 300es

one of 300es, 330, 410

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, cylinder

What to pick

shapes

circle, box, round-box, segment, polygon, star

— 2D

shapes

sphere, box, round-box, torus, capsule, cylinder, plane

— 3D

ops

union, subtract, intersect, their smooth- variants, round, onion, repeat, repeat-limited, extrude, revolve

— 2D + 3D

effects

fill, stroke, glow, shadow, inner-shadow

— 2D

Params

dim

2 (shapes to draw) or 3 (solids to raymarch)

default 2

one of 2, 3

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)

default 300es

one of 300es, 330, 410

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 --materials

Params

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

default false

materials

with --name: a material id per surface, for different colors

default false

lighting

with --name: lambert, blinn, toon or pbr (see glsl:lighting)

default lambert

one of lambert, blinn, toon, pbr

parts

with --into, comma-separated: march, normal, soft-shadow, ao, camera – or all

default 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)

default 300es

one of 300es, 330, 410

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)

default noise

one of noise, wobble, waves

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)

default 300es

one of 300es, 330, 410

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

required

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

required

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 simplexNoise and curlNoise.
  • BakingLab by MJP and David Neubelt, MIT: Stephen Hill's ACES fit, tonemapAces.
  • hash-prospector by Chris Wellons, public domain: the lowbias32 hash 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.