Skip to content

CRT material

<CrtMaterial map> is the material of the mesh it sits in, inside a World: an old cathode-ray screen showing map. The glass bends the picture toward the corners, scanlines run across it, a phosphor grille splits it into red, green and blue stripes, a bright bar rolls up it, and it flickers. All of it runs on the world’s clock, as every Shader does, so a paused game holds the bar where it is.

Its props are in CrtMaterialProps.

Unity, Unreal and Godot ship a CRT look as a screen-wide pass over the whole game. This one is a material, so a terminal or a television can stand in the world, and the player walks up to it. A screen-wide pass is not in the engine yet.

The shader is ThreeUI’s CRT by Meng To, MIT, moved from the whole canvas onto a mesh’s UVs. Its source is crtFragment, an exported GLSL string with a comment per block: copy it, change a block and pass it to a Shader for a screen of your own.

A terminal drawn on a canvas, standing on a plane in the meadow:

import { CrtMaterial, Entity, World } from "@daniel-zarinski/engine";
import { useMemo } from "react";
import { CanvasTexture } from "three";
function drawTerminal() {
const canvas = document.createElement("canvas");
canvas.width = 640;
canvas.height = 480;
const context = canvas.getContext("2d");
if (!context) throw new Error("no 2d context");
context.fillStyle = "#03100a";
context.fillRect(0, 0, canvas.width, canvas.height);
context.fillStyle = "#8dffb0";
context.font = "32px monospace";
context.fillText("> hello, world_", 40, 80);
return new CanvasTexture(canvas);
}
export function Terminal() {
const map = useMemo(drawTerminal, []);
return (
<World map="meadow">
<Entity position={[0, 1.2, -3]}>
<mesh>
<planeGeometry args={[1.6, 1.2]} />
<CrtMaterial map={map} />
</mesh>
</Entity>
</World>
);
}

Client. The screen changes nothing in the world; each player’s browser draws its own.