Examples
On this page

Examples

There are 98 runnable examples in the repository, from cartographic passes and the ShaderToy-era screen-space effects to compute kernels; 12 of them use emulated double precision. Browse the examples directory.

Learn TypeShade by building

Each step connects one Language Guide concept to actually writing a shader.

  1. 1. First shader

    Start with the shape of a "use typeshade" file and its shader entries.

    Quick start →
  2. 2. Values and types

    See how TypeScript type aliases and classes become GPU value layouts.

    Types →
  3. 3. Compose functions

    Combine helper functions with stage entries to build shader calculations.

    Functions →
  4. 4. GPU data

    Connect vectors, matrices, arrays, and host-owned shader resources.

    Resources →
  5. 5. Real examples

    Read the repository examples and combine the language pieces into complete shaders.

    Source examples →

MNIST training with TypeShade

A real-data softmax regression journey with forward, loss, gradients and SGD computed by TypeShade.

Explore the MNIST training journey →

Written as TypeScript source

Each example is a TypeScript file that opens with "use typeshade", compiled by compile() from the file's own bytes. Each one is there for one part of the language, and they are grouped here that way. 73 have a GLSL ES 3.00 form and the others emit WGSL alone. The ones the page can draw carry a picture.

Cartographic

Screen space

Stages and IO structs

Resources

Values and control flow

Classes and generics

Imports

Several passes

Module state and compute

Loops as kernels

Host examples

Each of these is two files: a "use typeshade" module and the host.ts that runs it, written the way an application writes it. The host imports the module through the Vite plugin and calls what it exports. Each page shows both files and the tiers the host runs on.

The gradient pass in GLSL ES 3.00

The gradient pass, gradient-twin.shade.ts, emits this GLSL ES 3.00 main for its fragment stage:

void main() {
float t = (uv.y + u.mix_bias);
vec3 rgb = mix(u.bottom.rgb, u.top.rgb, t);
_ret = vec4(rgb, 1.0);
}

The whole module is 30 lines of WGSL; the GLSL vertex stage is 17 lines and the fragment stage 17. Uniform types, byte offsets, bind-group entries and entry signatures come from reflect(), which reads the same intermediate representation and stays off the emit path, so a host can pack its uniform buffer from that layout. Verification shows the reflected layout of a uniform block beside the diagnostic for a misspelt field.

Emulated double precision

Neither WGSL nor GLSL ES 3.00 has a double-precision float. TypeShade emulates one as an unevaluated pair of f32 values, and lowers arithmetic, comparisons and a set of builtins into calls on that pair. An operation outside that set fails at emit with a coded error. What you write is unchanged: only the declared type differs. In the deep-zoom example the origin field is declared f64 in the source and occupies a vec2<f32> slot in the uniform block, which the host fills with the split pair.

A module that does f64 arithmetic also carries an injected guard texture binding for the host to fill with a single white texel. A downstream shader compiler cannot fold a texel read, and without one it is free to optimise the error-compensation terms away. Each operation costs several times its f32 equivalent, so the type is opted into per value. The example is fp64-deep-zoom-twin.shade.ts.

The guard belongs to the 'float' flavor of the emulation. An Apple GPU needs the 'integer' flavor, which computes the same terms through integer operations and binds no guard. The default is automatic selection from the actual device or context, with an available user-agent fallback. Apple or Metal signals select 'integer'; CPU and unknown GPU signals select 'float'. An unknown signal does not prove extended precision. createDevice({ fp64Flavor }) or configure({ fp64Flavor }) can override this choice. recommendFp64Flavor exposes the selector.

fp64 deep zoom (source twin) shader, written in TypeScript and compiled by TypeShade, rendered at build time.
One world coordinate drawn twice: f32 on the left, f64 (emulated) on the right. The f32 half draws the stripes as steps because one f32 ulp at this distance is coarser than the fraction a stripe needs; the other half draws them smooth.

Edit this page Report a problem