Show HN: a Rust, Wayland, Vulkan 3D CRT, screencast application, retroarch cores A developer released CRTULUM, a Rust-based Wayland and Vulkan application that renders a 3D CRT monitor on screen, captures other program windows or images, and runs RetroArch cores for emulation with real-time CRT effects. The app supports offline video rendering with scriptable camera moves and tube controls, achieving roughly 500 fps at 640×480 on an RX 9070 GPU. A CRT you can hold in your hands, minus the 70 pounds of leaded glass and the risk of the flyback transformer killing you in your garage. It's a Wayland app that grabs another program's output — RetroArch, a terminal, a browser, a video player, whatever — the same way OBS does window capture, and paints it onto a 3D Trinitron you can spin around with the mouse. Not a fullscreen filter. An actual tube, sitting in your compositor, that you can orbit and zoom until the glare slides across the glass the right way. Current Rust toolchain system rustup, stable . Then: cargo run -- --capture That pops the screencast picker. Point it at something. It lands on the tube. No window? Take a picture instead — handy when your compositor won't do wlr-screencopy and grim gives up: cargo run -- --shot out.png 1000x800 The other direction: put a game on the tube and pick up a controller. cargo run --release -- --play game.sfc cargo run --release -- --play game.cue --core swanstation --option swanstation GPU Renderer=Vulkan A libretro core runs in-process, one emulated frame per tick of the clock — not per monitor refresh, so a 59.727 Hz Game Boy and a 60.099 Hz SNES each run at their own speed whatever your display is doing. A gamepad is picked up automatically if one is plugged in; otherwise the keyboard stands in: | arrows | d-pad | | Z / X | B / A | | A / S | Y / X | | Q / W | L / R | | Enter · RShift | Start · Select | | F2 | pause the game the television keeps running | Every CRT control still works while you play — orbit the tube with the mouse, swap presets with the number keys, cut the power with P . The game's buttons take priority, so they can't also change the television. Sound comes from the core, resampled to whatever your audio device wanted. CRTULUM PLAY STATS=1 prints the emulated rate and how much audio is buffered, which is what to look at if it ever feels off. The other half of the app: hand it a source and it renders the whole thing through the tube and out the far side as a video file. Same shader, same phosphor planes, same presets — just offline, and pointed at ffmpeg instead of a window. any video file cargo run --release -- --render clip.mp4 out.mp4 a URL anything yt-dlp handles cargo run --release -- --render 'https://youtu.be/…' out.mkv --preset rca a directory of stills cargo run --release -- --render frames/ out.mp4 --fps 30 a ROM, with the run itself scripted frame-by-frame see below cargo run --release -- --render --rom smb.nes out.mp4 --script run.crts Audio comes along from the source. The tube is driven at 60 fields/sec no matter what frame rate you export at, so a 30 fps export still scans every frame twice and 480i still twitters. Signal resolution defaults to 480 lines --lines 240 for the real thing — a CRT never saw a 1080p signal, and feeding it one dissolves the scanline structure . --help for the rest: --size , --fps , --ssaa 3 by default, 1 for a fast preview , --start / --duration , --codec x264|x265|vp9|ffv1 , --crf , --no-audio . Roughly 500 fps at 640×480/ssaa 2 on an RX 9070 — most clips render faster than they play. A source alone gives you a static camera. A script gives you choreography — a flat timeline of camera moves, tube swaps, power cycles and degausses: size 1280x960 lines 240 preset trinitron camera yaw=0.55 pitch=0.22 dist=3.4 at 0:00 power on raster blooms open, auto-degauss at 0:03 camera to yaw=-0.35 dist=3.0 over 6 slow drift across the face at 0:12 preset pvm swap tubes mid-shot at 0:14 exposure to 1.25 over 2 at 0:30 spin 1 over 10 linear one full orbit at 0:52 power off collapse to a line, then a dot cargo run --release -- --render clip.mp4 out.mp4 --script examples/demo.crts Times are seconds or clock 0:03 , 1:02:30.5 . Moves take over