Settings

Settings other than “Default” will be saved in local storage to persist across pages, reloads, and sessions.

Dancing Dice

Did I mention that I love CSS?

There is no Javascript on this site that is in any way related to the dice. Go ahead, block it all!

So how does it work?

First we need to create the dice. These are just divs, containing spans (one per face):

<div class="die"><div class="cube"><span></span><span></span><span></span><span></span><span></span><span></span></div></div><div class="die"><div class="d8"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></div></div>

The demo on this site has ten dice of each kind.

Then we just add the following CSS, that makes uses a 3D-transformation to turn them into 3D-objects and translation and 3D-rotations to move them around. setting --step to be a property of numeric type allows us to smoothly increment it with CSS animations, and thats’ essentially it:

css dancing-dice.css Download
@property --step {  syntax: '<number>';  initial-value: 0;  inherits: true;}@keyframes animation-progress {	0%{--step:0.0;}	100%{--step:1.0;}}.dice {	animation-name: animation-progress;	animation-duration: 3600s;	animation-iteration-count: infinite;	animation-direction: repeat;	animation-timing-function: linear;	z-index: 1000;	height: 100%;	width: 100vw;	overflow: hidden;	top: 0;	left: 0;	position: absolute;	pointer-events: none;	.die {		position: absolute;		top: calc(50vh 			+  sin(sin(sin(sin((var(--step) * 100 + var(--index) * 10)))))  * 35vh			+  sin(sin(sin(sin((var(--step) * 200 + var(--index) * 15)))))  * 35vh			+  sin(sin(sin(sin((var(--step) * 300 + var(--index) * 20)))))  * 35vh			+  sin(sin(sin(sin((var(--step) * 400 + var(--index) * 25)))))  * 35vh		);		left: calc(50vw 			+  sin(sin(sin(sin((var(--step) * 100 + var(--index) * 11)))))  * 35vw			+  sin(sin(sin(sin((var(--step) * 200 + var(--index) * 16)))))  * 35vw			+  sin(sin(sin(sin((var(--step) * 300 + var(--index) * 21)))))  * 35vw			+  sin(sin(sin(sin((var(--step) * 400 + var(--index) * 26)))))  * 35vw		);		width: 14em;		height: 20em;		--index: calc(sibling-index());		& >* {			font-size: 4em;			width: 2em;			transform-style: preserve-3d;			transform:				translateY(2.5em)				perspective(20em)				rotateX(calc(var(--step) *  360deg * (193 + 3 * var(--index)) + var(--index) * 130deg))				rotateY(calc(var(--step) *  360deg * (367 + 5 * var(--index)) + var(--index) * 261deg))				rotateY(calc(var(--step) *  360deg * (467 + 7 * var(--index)) + var(--index) * 73deg))				;			counter-reset: side-counter 0;			& >* {				counter-increment: side-counter 1;				&:before{content: counter(side-counter);}				position: absolute;				top: 0;				left: 0;				width: 2em;				height: 2em;				color: var(--accent-col);				background: oklch(from var(--accent-inv-col-2) l c calc(var(--index) * 113) / 0.6);				border: var(--border-width) solid var(--accent-col);				text-align: center;				line-height: 2em;			}			&.cube >* {				&:nth-child(1){transform: translateZ(1em);}				&:nth-child(2){transform: rotateX(90deg)   translateZ(1em);}				&:nth-child(3){transform: rotateY(90deg)   translateZ(1em);}				&:nth-child(4){transform: rotateY(-90deg)  translateZ(1em);}				&:nth-child(5){transform: rotateX(-90deg)  translateZ(1em);}				&:nth-child(6){transform: rotateY(-180deg) translateZ(1em);}			}			&.d8 >* {				--h: calc(sqrt(3) / 2 * 100%);				clip-path: polygon(0 var(--h), 50% 00%, 100% var(--h));				--dist: calc(2em / sqrt(6));				--γ: calc(90deg - atan(sqrt(2)));				--nγ: calc(0deg - var(--γ));				--dy: -0.15em;				&:nth-child(1){transform: rotateX(var(--γ)) translateZ(var(--dist)) translateY(var(--dy));}				&:nth-child(2){transform: rotateX(var(--nγ)) rotateZ(180deg) translateZ(var(--dist)) translateY(var(--dy));}				&:nth-child(3){transform: rotateY(92deg) rotateX(var(--γ)) translateZ(var(--dist)) translateY(var(--dy));}				&:nth-child(4){transform: rotateY(92deg) rotateX(var(--nγ)) rotateZ(180deg) translateZ(var(--dist)) translateY(var(--dy));}				&:nth-child(5){transform: rotateY(-92deg) rotateX(var(--γ)) translateZ(var(--dist)) translateY(var(--dy));}				&:nth-child(6){transform: rotateY(-92deg) rotateX(var(--nγ)) rotateZ(180deg) translateZ(var(--dist)) translateY(var(--dy));}				&:nth-child(7){transform: rotateY(180deg) rotateX(var(--γ)) translateZ(var(--dist)) translateY(var(--dy));}				&:nth-child(8){transform: rotateY(180deg) rotateX(var(--nγ)) rotateZ(180deg) translateZ(var(--dist)) translateY(var(--dy));}			}		}	}}

Except that Firefox sadly does not yet support sibling-index and that we cannot use counters, because they only work on strings and we don’t have a way to turn them to numbers… Now, as much as I dislike Mozilla, their browser is currently the only one that doesn’t use Webkit (and is furthermore what I use), so I’ve for the moment added the following fallback:

.dice {	/*Firefox Fallback:*/	.die:nth-child(1){--index:1;}	.die:nth-child(2){--index:2;}	.die:nth-child(3){--index:3;}	/* … */}