_❯Film Grain Shader

Procedural multi-octave value-noise film grain in WebGL, supersampled to stay fine on high-DPI screens.

WebGLGLSLfilm grainnoise
1import { useState } from "react";
2import { GrainCanvas } from "@/components/misc/grain-canvas/grain-canvas.component";
3import { LabsDemoLayout } from "../../components/labs-experiment-frame/labs-experiment-frame.component";
4import {
5 ControlGroup,
6 ControlPanel,
7 ResetButton,
8 SliderControl,
9} from "../../components/labs-control/labs-control.component";
10import * as styles from "./grain-shader.demo.css";
11
12const DEFAULT_GRAIN = 0.18;
13const DEFAULT_BASE = 0.04;
14
15export function GrainShaderDemo() {
16 const [grainAlpha, setGrainAlpha] = useState(DEFAULT_GRAIN);
17 const [baseAlpha, setBaseAlpha] = useState(DEFAULT_BASE);
18
19 const reset = () => {
20 setGrainAlpha(DEFAULT_GRAIN);
21 setBaseAlpha(DEFAULT_BASE);
22 };
23
24 return (
25 <LabsDemoLayout
26 stage={
27 <div className={styles.stageInner}>
28 <div className={styles.grainBox}>
29 <GrainCanvas grainAlpha={grainAlpha} baseAlpha={baseAlpha} />
30 </div>
31 </div>
32 }
33 controls={
34 <ControlPanel>
35 <ControlGroup title="Grain">
36 <SliderControl
37 label="Grain"
38 value={grainAlpha}
39 min={0}
40 max={0.4}
41 step={0.005}
42 onChange={setGrainAlpha}
43 format={(value) => value.toFixed(3)}
44 />
45 <SliderControl
46 label="Base"
47 value={baseAlpha}
48 min={0}
49 max={0.15}
50 step={0.005}
51 onChange={setBaseAlpha}
52 format={(value) => value.toFixed(3)}
53 />
54 </ControlGroup>
55
56 <ResetButton onReset={reset} />
57 </ControlPanel>
58 }
59 />
60 );
61}