film-grain
Drop-in film/TV grain overlay for any web UI — canvas-rendered noise scrambled per-frame at 24fps, not a sliding tile. Sub-pixel density on HiDPI displays, `overlay` blend mode so it adds texture without obscuring content. Trigger when the user wants to add film grain, TV static, noise overlay, or analog texture to a website, web app, CEP panel, or HTML page. Also triggers on "make it look filmic", "add grain", "SYSTEM grain", "GIR grain", or when applying the SYSTEM//02 design language.
CLI INSTALL
curl -sS https://dem0n.vip/s/gualo/film-grain/SKILL.md -o ~/.claude/skills/film-grain/SKILL.md --create-dirs
DOWNLOAD ALL gives you a single .zip containing SKILL.md + the tar.gz — drag it into Claude Code in one go.
Sign up to see the full skill
Get the source, install command, comments, and version history
GET AN INVITEFILM GRAIN — Drop-in Film Grain for Web UIs
By Gualo Hawes. Ships with SYSTEM//02 but works standalone for any web surface.
Why this exists
Most web grain implementations tile an SVG noise pattern and translate it on a CSS animation. That produces a visible "sliding box" artifact — you see the tile edges shimmering across the viewport. Real film and TV grain scrambles the entire frame each tick; nothing slides.
This skill is a 50-line canvas-based grain engine that:
- Renders fullscreen canvas noise
- Re-randomizes every pixel each refresh (default 24fps = film)
- Runs at sub-pixel density on HiDPI (canvas density × device pixel ratio)
- Uses
overlayblend mode so text and UI stay readable - Self-installs — one
<script>tag and it's running
Usage
1. Drop in
Copy grain.js into your project and add to any page:
<script src="grain.js"></script>
That's the whole install. It creates a fixed fullscreen canvas at z-index: 900, pointer-events: none, starts the grain loop automatically.
2. Configure (optional)
Set window.FILM_GRAIN_CONFIG before loading the script:
<script>
window.FILM_GRAIN_CONFIG = {
opacity: 0.16, // 0..1 — default 0.16
fps: 24, // refresh rate — 24 = film, 30 = TV, 12 = cinéma vérité
blend: 'overlay', // any CSS mix-blend-mode
zIndex: 900,
id: 'grain',
dpr: 2, // max canvas density (2 = Retina-tight, 1 = chunkier)
};
</script>
<script src="grain.js"></script>
3. Runtime control
After load, the public API:
FilmGrain.setOpacity(0.10); // softer
FilmGrain.setBlend('soft-light');
FilmGrain.setFps(12); // slower refresh
FilmGrain.destroy(); // remove
Recommended settings
| Surface | opacity | blend | fps | notes |
|---|---|---|---|---|
| Light UI (default) | 0.16 | overlay | 24 | SYSTEM silver |
| Dark UI | 0.18 | overlay | 24 | SYSTEM black |
| Over imagery | 0.22 | overlay | 24 | product pages |
| Subtle / readable | 0.10 | soft-light | 24 | long-form content |
| Heavy cinematic | 0.28 | overlay | 24 | hero sections |
Behavior
- Zero DOM footprint except one
<canvas id="grain"> - Zero pointer interference —
pointer-events: none - No input-size bundle — grain is generated per frame, not a huge noise PNG
- HiDPI-aware — canvas scales to device pixel ratio (capped at
dprconfig) - Window resize — automatically resizes canvas on viewport changes
- Animation frame loop — pauses when tab is backgrounded (browser default)
When NOT to use
- Don't stack multiple grain layers — one layer is correct film grain; two looks muddy
- Don't animate the
opacityvia CSS transitions — the canvas is already animating - Don't set
z-indexbelow your content if you want grain to overlay (default 900 works for most cases) - For e-readers or text-heavy docs, drop opacity to 0.08 or skip entirely — grain reduces readability at long lengths
Performance
- ~1.5ms per frame at 1440p / DPR 2 (fill imageData + putImageData)
- No GPU shader cost
- Runs on mobile (iOS Safari, Chrome Android) without jank
File contract
film-grain/
SKILL.md ← this file
grain.js ← the engine (self-contained, ~50 lines)
Trigger phrases
- "add film grain to this site"
- "add TV static / noise overlay"
- "make this look filmic / analog"
- "add the SYSTEM grain"
- "use Gualo's grain / GIR grain"
- "add grain to my landing page / web app / CEP panel"
When triggered: copy grain.js to the user's project (usually public/ or assets/), add the <script> tag to their root layout or index.html, and confirm with the recommended settings table above.
Version
- v1.0 — April 2026 (authored by Gualo Hawes)
- Extracted from SYSTEM//02 preview — the exact grain used on demonhub.
BADGE

VERSIONS
- 1.0.0 — 3.6 KB — 157d5e325cd9
COMMENTS (0)
LOGIN TO COMMENT