Skip to content

Pixel Shader (Three.js)

A small scene of boxes around a glowing, turning crystal, pushed through Three.js’s EffectComposer with the RenderPixelatedPass addon: chunky retro pixels with clean edges.

The teapot showcase draws in a single forward pass. This one renders into off-screen targets, runs custom GLSL for the pixel-art edge detection, and blits the result to the screen. It is the demo to look at if your app needs multi-pass rendering, render targets or shaders of its own.

Ported from the upstream webgl_postprocessing_pixel example.

It declares all four runtimes, so pick the one you have:

Terminal
gjsify showcase three-postprocessing-pixel --runtime gjs
gjsify showcase three-postprocessing-pixel --runtime node
gjsify showcase three-postprocessing-pixel --runtime bun
gjsify showcase three-postprocessing-pixel --runtime deno

A GTK 4 window opens as an Adw.OverlaySplitView with the controls in the sidebar. Pixel Size crushes the resolution into bigger cells. Normal Edge and Depth Edge set how strongly the shader outlines geometry. Pixel-Aligned Panning snaps camera movement to the pixel grid so the image stops shimmering as it moves. Same window and same pipeline on all four.

--runtime gjs runs the --app gjs bundle against GJS’s native gi://. The other three share one --app node bundle, where gi:// goes through the node-gi bridge. Leave the flag off and Choose the runtime says what the CLI picks.

TypeScript
import { mount } from '@gjsify/example-dom-three-postprocessing-pixel/browser';
const handle = mount(document.getElementById('app')!, {
assetBase: '/demos/pixel/', // where checker.png is served from
});
handle.pause();
handle.resume();

The browser build rebuilds the same sidebar from @gjsify/adwaita-web custom elements.

The composer chain is the standard Three.js one, and nothing in it changes for the GTK window:

  1. EffectComposer wraps the renderer and owns the off-screen targets.
  2. RenderPixelatedPass renders the scene at a reduced resolution and applies the edge detection, with its own GLSL compiled at runtime.
  3. OutputPass blits the result to the screen framebuffer.

Each stage carries its own buffer and texture state, which is the part @gjsify/webgl has to track for you.

PackageWhat it needs
@gjsify/webglOff-screen framebuffers, shaders compiled at runtime, OrbitControls
@gjsify/dom-elementsHTMLCanvasElement and requestAnimationFrame
@gjsify/adwaita-webThe sidebar in the browser build
LibadwaitaAdw.SpinRow and Adw.SwitchRow in the GTK build

showcases/dom/three-postprocessing-pixel/

src/three-demo.ts holds the scene and the whole post-processing chain behind one start(canvas) function. Both targets reuse it; only the shell around the canvas differs, in src/gjs/ and src/browser/.