It’s raining somewhere.

How it Works

As a disclaimer, I didn’t come up with this entirely from scratch. The original idea (and most of the implementation) comes from this CodePen. I simply restructured it to not require a pre-processor for the code, and also so that it can be added to the Midnight if desired.

The underlying idea is pretty ingenuous. It uses a single SVG for the individual raindrops, and then duplicates it however-many times. But individual copies wouldn’t be enough, of course: you’d have to either animate each one by hand or add a lot of code to spread them out.

Instead, the creator took advantage of CSS properties, which basically work as variables. For each drop, the basic “algorithm” is coded into the CSS, but combined with a custom property. That custom property is then set via JavaScript when the drops are created. The JavaScript code handles (pseudo-)random number generation and actually duplicating the individual raindrops.

Using on Your Site

As I said, my main reason for refactoring the original code was to allow its use on the Midnight (as well as without requiring a pre-processor more generally). I also added the button to turn it off and on just in case. I made a couple minor tweaks as well, namely making the drops about 10 pixels shorter than the original (which also made them narrower due to how the SVG is put together); I simply thought this looks better. I also added some additional CSS properties for easier customization.

Anyway, to add this effect to your Midnight template (such that you’ll see it on every page of the Midnight proper), go to your site, look for a file called _theme.css, and click the edit button next to it. Then paste the following code at the bottom of the text box this takes you to (it has to be at the bottom since it involves going beyond simple CSS styles). Note that this is the “non-button” version, meaning it will run all the time every time. Adding the button is a tad more complicated, mainly in that you need to put the button someplace where it makes sense, and so there’s not really a one-size-fits-all solution.

:root {
	--dropcolor: #a1c6cc;
	--dropheight: 20px;
	--traveltime: 1s;
}

#rain-machine {
	position: fixed;
	inset: 0;
	z-index: -1;
	height: 100vh;
	width: 100vw;
	overflow: hidden;
}

.raindrop {
	-webkit-animation-delay: calc(var(--d) * 1s);
	animation-delay: calc(var(--d) * 1s);
	-webkit-animation-duration: calc(var(--a) * var(--traveltime));
	animation-duration: calc(var(--a) * var(--traveltime));
	-webkit-animation-iteration-count: infinite;
	animation-iteration-count: infinite;
	-webkit-animation-name: drop;
	animation-name: drop;
	-webkit-animation-timing-function: linear;
	animation-timing-function: linear;
	height: var(--dropheight);  
	left: calc(var(--x) * 1%);
	position: absolute;
	top: calc((var(--y) + 50) * -1px);
	pointer-events: none;
}

.raindrop path {
	fill: var(--dropcolor);
	opacity: var(--o);
	transform: scaleY(calc(var(--s) * 1.5));
}

@-webkit-keyframes drop {
	90% {
	    opacity: 1;
	}
	100% {
	    opacity: 0;
	    transform: translateY(100%);
	}
}

@keyframes drop {
	90% {
		opacity: 1;
	}
	100% {
    	opacity: 0;
    	transform: translateY(100%);
  	}
}

<script type="text/javascript">

window.addEventListener('load', makeItRain);

function makeItRain() {
	const dropCount = 250;	// how many drops to render
	const rainContainer = document.getElementById('rain-machine');
	const dropPath = 'M 2.5,0 C 2.6949458,3.5392017 3.344765,20.524571 4.4494577,30.9559 5.7551357,42.666753 4.5915685,50 2.5,50 0.40843152,50 -0.75513565,42.666753 0.55054234,30.9559 1.655235,20.524571 2.3050542,3.5392017 2.5,0 Z';
	const ns = 'http://www.w3.org/2000/svg';
	
	for (let i = 0; i < dropCount; i++) {
		
		let x = Math.floor(Math.random() * 100);
		let y = Math.floor(Math.random() * 100);
		let o = Math.random();
		let a = Math.random() + 0.5;
		let d = (Math.random() * 2) - 1;
		let s = Math.random();
		
		let styles = `--x: ${x}; --y: ${y}; --o: ${o}; --a: ${a}; --d: ${d}; --s: ${s}`;

		let newdrop = document.createElementNS(ns, 'svg');
		newdrop.setAttribute('class', 'raindrop');
		newdrop.setAttribute('preserveAspectRatio', 'xMinYMin');
		newdrop.setAttribute('viewbox', '0 0 5 50');
		newdrop.setAttribute('style', styles);
		
		let droplet = document.createElementNS(ns, 'path');
		droplet.setAttribute('stroke', 'none');
		droplet.setAttribute('d', dropPath);
		
		newdrop.append(droplet);
		rainContainer.append(newdrop);
		
	}
}
</script>

Customizing

I tried to make things a little more customizable as well. The easiest things to modify are: the raindrop color, its size, and how long it takes to travel the height of the screen. These three options are at the beginning of the code above. If you decide to change any of these, make sure you leave the formatting as-is. In other words, there needs to be a hash symbol (#) in front of the color (unless you use a different scheme, e.g. rgb()), the abbreviation “px” needs to be after the droplet height, etc.

For the color value, this needs to be something recognized by CSS. You can use an online picker tool to get the “code” needed. With the one I linked, you’ll see three boxes in the top right corner, labeled RGB, HSL, and HEX. Any one of these can be used, provided you include everything from the box so the browser knows which method you’re using.

If you want to change how many drops are used, just look for this line: const dropCount = 250; // how many drops to render. You can change the number to whatever you want (just make sure the semi-colon stays put), but bear in mind that the larger the number, the more computationally demanding the page will be. If you want to make the rainfall seem thicker, there are probably better ways such as slowing them down and making the drops larger. But a larger number can certainly help as well.

Back to the box