CRT Screen

Put a scene behind curved glass — barrel distortion, phosphor mask, scanlines and a falling-away rim

Installation
$ pnpm dlx shadcn@latest add @remocn/crt-screen

Usage

CrtScreen is a wrapper. Put a scene inside it and every frame is re-read through the geometry of a tube: the picture bulges outward so straight lines bow, an RGB phosphor mask breaks the colour into stripes, scanlines follow the same curve rather than lying flat across the frame, bright areas flood the mask around them, and the corners run off the glass into darkness.

It has no duration of its own — it lasts exactly as long as the scene it wraps.

tsx
import { CrtScreen } from "@/components/remocn/crt-screen";
 
export const MyScene = () => (
  <CrtScreen>
    <Scene />
  </CrtScreen>
);

Keeping content on the glass

The warp pushes the corners of the frame outside the picture, which is what produces the dark rim — and what will clip anything parked there. Give the scene inside a generous margin, or drop curvature toward 0 for a flat-panel look that keeps the mask and scanlines.

tsx
<CrtScreen curvature={0.35}>
  <Scene />
</CrtScreen>

Props5

PropTypeDefault
curvature
number1

How far the tube bulges. Bends the picture more across the vertical than the horizontal, the way a real tube is shaped. At 0 the frame stays flat and nothing is clipped at the corners.

scanlines
number1

Strength of the horizontal line structure. The lines follow the curvature rather than lying flat, so they bow with the picture.

maskScale
number2

Width in device pixels of one phosphor stripe, so a triad is three times this. Low values read as a fine aperture grille, high values as a coarse arcade tube. Measured in output pixels, so the mask gets finer relative to the frame as you render larger.

vignette
number1

How hard the picture falls off toward the rim, where the glass curves away from the viewer.

children
React.ReactNode—

The scene the filter wraps and re-reads every frame.

  • curvaturenumber

    How far the tube bulges. Bends the picture more across the vertical than the horizontal, the way a real tube is shaped. At 0 the frame stays flat and nothing is clipped at the corners.

    Default1

  • scanlinesnumber

    Strength of the horizontal line structure. The lines follow the curvature rather than lying flat, so they bow with the picture.

    Default1

  • maskScalenumber

    Width in device pixels of one phosphor stripe, so a triad is three times this. Low values read as a fine aperture grille, high values as a coarse arcade tube. Measured in output pixels, so the mask gets finer relative to the frame as you render larger.

    Default2

  • vignettenumber

    How hard the picture falls off toward the rim, where the glass curves away from the viewer.

    Default1

  • childrenReact.ReactNode

    The scene the filter wraps and re-reads every frame.

Notes

Outside the tube the filter writes opaque black rather than nothing, so the rim is real darkness and not a hole showing the untouched scene through it. Inside the tube the scene's own transparency is preserved, so put a background within the wrapper rather than behind it.

The only thing that moves is a 1% brightness breath on a 60-frame cycle, which closes seamlessly on any loop that is a multiple of it. Everything else is fixed geometry, so two renders of the same frame produce the same pixels and the look holds for as long as the scene runs.