React Three Fiber
pnpm add crt-shader react@19 react-dom@19 three@^0.186.0 postprocessing@^6.39.5 @react-three/fiber@^9.7.0 @react-three/postprocessing@^3.1.1Live example · Click to inspect
Save as CRTScene.jsx, then import and render <CRTScene /> in your existing React app.
'use client';
import { Canvas } from '@react-three/fiber';import { EffectComposer, ToneMapping } from '@react-three/postprocessing';import { HalfFloatType } from 'three';import { ToneMappingMode } from 'postprocessing';import { CRT } from 'crt-shader/r3f';
export default function CRTScene({ onCreated, crtRef }) { return ( <Canvas flat frameloop="demand" dpr={1} camera={{ position: [0, 0, 5], fov: 40 }} gl={{ antialias: false, preserveDrawingBuffer: true }} style={{ width: '100%', aspectRatio: '8 / 5' }} onCreated={onCreated} > <color attach="background" args={['black']} /> <mesh> <torusKnotGeometry args={[0.8, 0.25, 128, 16]} /> <meshNormalMaterial /> </mesh> <EffectComposer frameBufferType={HalfFloatType} multisampling={0}> <ToneMapping mode={ToneMappingMode.LINEAR} /> <CRT ref={crtRef} preset="reference" inputResolution={160} /> </EffectComposer> </Canvas> );}Fiber owns the renderer and mesh resources; CRT handles its pass cleanup on unmount.
Fiber 9 requires React 19. Keep one copy of React, React DOM, Three.js and postprocessing in your app.
Pass, not material
Section titled “Pass, not material”CRT processes the full composer rectangle. Place scene effects before tone mapping and keep CRT last. <Canvas flat> disables renderer tone mapping; the composer’s ToneMapping supplies it. For an existing Canvas, move the mesh and composer inside it rather than creating another Canvas.
Control preparation and reconstruction
Section titled “Control preparation and reconstruction”inputResolution="auto" chooses a reduced input size; a positive integer sets the longest edge, capped at native resolution. mode="pixelated" shows prepared input and mode="original" bypasses the effect.
Update settings through props, such as exposure={1.05}. The optional crtRef in this example exposes the pass; after imperative updates in a demand-rendered scene, call Fiber’s invalidate(). For animation, switch frameloop to "always" and update your scene with useFrame.