Skill: Add a CRT or post-process effect
Stack fullscreen post-process effects (CRT curvature, scanlines, bloom, glitch) on top of the finished frame, which runs on WebGPU only. Use for a retro CRT...
Written for blit386 1.8.0. The rest of blit386.dev documents the newest engine, so an API you find there may be missing from a game pinned to 1.8.0.
Stack fullscreen effects (CRT curvature, scanlines, bloom, glitch) on top of the finished frame. These run on WebGPU only.
When to use
Use for a retro CRT look, screen glitches, bloom or glow, or any whole-screen filter.
Step 1: a bigger output buffer
Display-tier effects (curvature, scanlines, mask, bloom) need an output buffer larger than the logical screen, set in
configure():
configure() {
return {
displaySize: new Vector2i(320, 240), // you draw here
drawingBufferSize: new Vector2i(1280, 960), // effects render here (required)
};
}Step 2: guard the software fallback, then add effects
Post-process throws on the Canvas 2D fallback, so check the active backend first:
async init() {
// ...palette and setup...
if (BT.activeBackend === 'webgpu') {
this.crt = BT.preset.crtPipBoy(); // a ready-made array of CRT effects
for (const fx of this.crt) BT.effectAdd(fx);
}
return true;
}Turn it off again (guard the same way - this.crt is never set on the software fallback):
if (this.crt) {
for (const fx of this.crt) BT.effectRemove(fx);
}
// or remove everything at once:
BT.effectClear();Step 3: respect reduced motion (engine 1.7.0+)
Some players ask their system for less motion because heavy movement makes them dizzy or unwell. The moving effects are
the first thing to tone down: Flicker, Interference, RollLine, Noise, and PixelGlitch all animate, and a
flickering, rolling CRT is exactly what that setting exists to suppress. Static curvature, scanlines, and vignette are
fine to leave on.
import { BT, Flicker, Interference, Noise, RollLine } from 'blit386';
async init() {
if (BT.activeBackend === 'webgpu') {
this.crt = BT.preset.crtPipBoy();
this.applyMotionPreference();
}
return true;
}
applyMotionPreference() {
if (!this.crt) return;
// Keep the still parts of the look, drop the ones that move.
// crtPipBoy() ships four animating effects; the rest (curvature, aberration, scanlines,
// mask, bloom) hold still and are fine to leave on.
const moving = [Flicker, RollLine, Interference, Noise];
for (const fx of this.crt) BT.effectRemove(fx);
for (const fx of this.crt) {
if (BT.isReducedMotionPreferred && moving.some((Kind) => fx instanceof Kind)) continue;
BT.effectAdd(fx);
}
}
// Optional. Runs if the player changes the setting while the game is open.
onReducedMotionChange() {
this.applyMotionPreference();
}BT.isReducedMotionPreferred is a getter (no parentheses). Screen shake you wrote yourself deserves the same
treatment - clamp its amplitude to 0 when the flag is set.
Key calls
BT.effectAdd(effect)/BT.effectRemove(effect)/BT.effectClear()- methods.BT.isReducedMotionPreferred(getter, engine 1.7.0+) - true when the player asked for less motion; optionalonReducedMotionChange(prefersReduced)on your game class fires when that changes mid-session.- Presets (functions on
BT.preset):BT.preset.crtPipBoy(),BT.preset.amber(),BT.preset.green()- each returns a fresh array of effects. - Effect classes you can construct, tweak, then
effectAdd: display-tierScanlines,BarrelDistortion,Bloom,Vignette,RGBMask,ChromaticAberration,Noise,Flicker,Interference,RollLine; pixel-tierPixelGlitch,PixelMosaic.
import { Scanlines } from 'blit386';
const lines = new Scanlines();
lines.strength = 0.4;
BT.effectAdd(lines);Notes
- Always gate effects behind
BT.activeBackend === 'webgpu'so the game still runs on the software fallback. Keep starter games effect-free. - Gate on
BT.activeBackend, never onBT.requestedBackend.requestedBackendis the backend you asked for, and it still says'webgpu'even after the engine has quietly fallen back to Canvas 2D - so a guard built on it passes on exactly the machines it was meant to protect, andBT.effectAddthrows there. - Some effects animate from a
timefield - setfx.time = BT.timeSecondseach frame. - Hold onto effect instances and reuse them; rebuilding every toggle re-allocates GPU pipelines.
- Full effect reference lives in the engine repo (linked from
AGENTS.md); the local docs do not cover every effect.
When something breaks
Every programmer breaks their game many times a day. It is a normal part of making things, not a sign you did something wrong. This page walks you through...
Skill: Add a sprite
Load a PNG sprite sheet into the palette and draw it, whole image or single frames. Use whenever the user wants an image, character, tile, or animated...