Raindrop FX
Optimised raindrop effect on glass with WebGL2
Inspired by https://github.com/codrops/RainEffect
Check the live demo: https://lab.sardinefish.com/rain
Background image from https://www.pixiv.net/artworks/84765992
Usage
Use as npm package
You might need some bundle tools to run it in browser. eg. esbuild
or webpack
$ npm install raindrop-fx
const RaindropFX = require("raindrop-fx");
const canvas = document.querySelector("#canvas");
const rect = canvas.getBoundingClientRect();
canvas.width = rect.width;
canvas.height = rect.height;
const raindropFx = new RaindropFX({
canvas: canvas,
background: "path/to/background/image",
});
window.onresize = () =>
{
const rect = canvas.getBoundingClientRect();
raindropFx.resize(rect.width, rect.height);
}
raindropFx.start();
In Browser
For directly use in browser, download the pre-bundled script at /bundle/index.js
and embed with your Web page.
<!DOCTYPE html>
<html lang="en">
<head>
<title>Raindrop Effect</title>
<link rel="stylesheet" href="assets/css/style.css">
<script src="./bundle/index.js"></script>
</head>
<body>
<div id="root">
<canvas id="canvas"></canvas>
</div>
<script src="./script.js"></script>
</body>
</html>
style.css
body {
margin: 0;
}
#root {
position: absolute;
width: 100vw;
height: 100vh;
}
#canvas {
width: 100%;
height: 100%;
}
script.js
const canvas = document.querySelector("#canvas");
const rect = canvas.getBoundingClientRect();
canvas.width = rect.width;
canvas.height = rect.height;
const raindropFx = new RaindropFX({
canvas: canvas,
background: "path/to/backgroundImage",
});
raindropFx.start();
window.onresize = () =>
{
const rect = canvas.getBoundingClientRect();
raindropFx.resize(rect.width, rect.height);
}
Configuration
All options except canvas element are optional at initialize.
const raindropFx = new RaindropFX({
canvas: canvas,
});
All options can be set dynamically by raindropFx.options.xxx=xx
except renderer size and background image
Example:
const raindropFx = new RaindropFX({
canvas: canvas,
spawnSize = [30, 80],
spawnInterval = [0.1, 0.2],
mistBlurStep: 5,
dropletsPerSecond: 1000,
});
await raindropFx.start();
raindropFx.options.spawnSize = [30, 150];
raindropFx.options.spawnInterval = [0.01, 0.05];
raindropFx.options.mistBlurStep = 5;
raindropFx.options.dropletsPerSecond = 1000;
Resize
Call raindropFx.resize
to properly reszie.
raindropFx.resize(someWidth, someHeight);
Background
Background image must be set by calling to raindropFx.setBackground
.
Background image source could be a url string, HTMLCanvasElement
, HTMLImageElement
, ArrayBuffer
and other types that implemented interface TexImageSource
and ArrayBufferView
.
await raindropFx.setBackground("background image url");
const img = new Image();
await raindropFx.setBackground(img);
const anotherCanvas = document.querySelector("#another-canvas");
await raindropFx.setBackground(anotherCanvas);
Raindrop Simulation Options
interface SimulationOptions
{
spawnInterval: [number, number],
spawnSize: [number, number],
spawnLimit: number,
slipRate: number,
motionInterval: [number, number],
xShifting: [number, number],
colliderSize: number,
trailDropDensity: number,
trailDropSize: [number, number],
trailDistance: [number, number],
trailSpread: number,
initialSpread: number,
shrinkRate: number,
velocitySpread: number,
evaporate: number,
gravity: number,
}
Rendering Options
interface RenderingOptions
{
backgroundBlurSteps: number,
mist: boolean,
mistColor: [number, number, number, number],
mistTime: number,
mistBlurStep: number,
dropletsPerSeconds: number,
dropletSize: [number, number],
smoothRaindrop: [number, number],
refractBase: number,
refractScale: number,
raindropCompose: "smoother" | "harder"
raindropLightPos: [number, number, number, number],
raindropDiffuseLight: [number, number, number],
raindropShadowOffset: number,
raindropEraserSize: [number, number],
raindropSpecularLight: [number, number, number],
raindropSpecularShininess: number,
raindropLightBump: number,
}
Performance
By default settins on a 1920x1080 screen, there are up to 600 raindrops on the screen, which taks 2~3ms to update each frame.