@robertmyrsater/komorebi
v0.2.0
Published
Super-realistic dappled light for websites — real shadow photography, wind sway, a slow sun cycle. Framework-agnostic WebGL engine with a React wrapper.
Maintainers
Readme
Komorebi
Live: https://komorebi.myrsater.xyz
komorebi (木漏れ日) — sunlight filtering through leaves; the shifting lace of light and shadow it casts.
A super-realistic dappled light experience for the web. A WebGL canvas overlays the page and composites real shadow photography with noise-driven domain warping (wind sway), a slow sun cycle (drifting warmth, angle and softness), and per-scene tone grading. The demo page is a dictionary entry under the moving light, with live tuning controls.
Install (npm)
npm i @robertmyrsater/komorebi
import { Komorebi } from '@robertmyrsater/komorebi';
new Komorebi({ scene: 'bands' });
// React / Next.js ('use client'):
import { KomorebiLight } from '@robertmyrsater/komorebi/react';
<KomorebiLight scene="bands" />Then copy the textures your scenes use into your site's static root:
cp -r node_modules/@robertmyrsater/komorebi/textures public/texturesThe 9 wall textures from the demo (plasters, papers, the grain tile) ship too, if your page wants a surface for the light to fall on:
cp -r node_modules/@robertmyrsater/komorebi/backdrops public/backdropsRun the demo
npm install
npm run devThe engine
src/komorebi.ts is framework-agnostic and dependency-free:
import { Komorebi } from './src/komorebi';
const light = new Komorebi({
scene: 'grove', // any preset from src/scenes.ts (14 included)
blendMode: 'multiply', // 'multiply' (text-safe) | 'hard-light' (glowing)
intensity: 1, // global shadow strength
sunCycleSeconds: 150, // full sun drift cycle
renderScale: 0.6, // canvas resolution fraction (perf)
});
light.setScene('palm'); // cross-fades between presets
light.setTuning({ speed: 2 }); // live: strength, warp, speed, softness,
// sunPhase, gamma, floor, blendMode
light.pause(); light.resume(); light.destroy();Baseline tuning lives in the exported DEFAULT_TUNING (wind speed and
sway warp default to full strength).
In React/Next.js, mount it in an effect:
useEffect(() => {
const light = new Komorebi({ scene: 'grove' });
return () => light.destroy();
}, []);Serve public/textures/ from your site. Each scene is a single shadow
texture shown once (cover-fit, never tiled); presets live in src/scenes.ts
with per-layer sway/drift parameters and an optional tone grade.
The demo page
- Scene and Wall pickers with thumbnail previews (14 light scenes, 9 wall textures — plasters, wood, concrete, papers, and a plain grain default)
- Tuning panel: time of day, wind, sway, shadows, softness, contrast, shade, and Shade/Glow blending — "Copy" exports the current values as JSON for baking back into presets
- Hand-rolled UI kit (
src/demo/ui.ts): skinned native slider, segmented control, preview select (ARIA listbox), animated popovers - Icons: scribbles-icons ink weight, inlined SVG
- Type: Newsreader (editorial serif), IBM Plex Mono (controls), Noto Serif JP (the 木漏れ日 glyphs, subsetted)
Textures
npm run textures regenerates all processed WebPs with sharp. It reads the
licensed source packs (Bracken Shadow Overlays, Backdrop Textures, Macro
Paper) from paths configured at the top of scripts/prepare-textures.mjs —
the packs themselves are not in the repo; the processed derivatives are, so
the demo runs from a clone.
Performance
One draw call per frame at ~0.6× resolution (DPR capped at 1.5), in-shader
noise, 4-tap softness blur, mipmapped POT textures. Pauses when the tab is
hidden; honors prefers-reduced-motion; no-ops gracefully without WebGL.
Measured: 120fps on a mid-range laptop, ~2MB stable heap.
Credits
- Shadow photography, walls and paper: Bracken (licensed; source packs not for redistribution)
- Icons: scribbles-icons
