Dice roll
CSS + JSJS picks a random face and adds full turns; a CSS transition does the tumbling.
A 3×3×3 cube built as three layers that take turns twisting a quarter turn. Each layer is one flat box with the stickers painted on by gradients.
Your edited version
-11s and -10s shift layers 2 and 3 one and two seconds later, so they take turns.backface-visibility: hidden: the two touching faces point in opposite directions, so only one is ever drawn and they never flicker.--u, tied to the canvas, so the cube and the black lines between its stickers are the same share of a gallery card, the editor and a recording canvas.one box per layer, not 27 cubeletsgradient sticker gridshared keyframes + negative delays take turnsbackface-visibility on inner faces<div class="scene">
<div class="rubik">
<div class="layer"><i></i><i></i><i></i><i></i><b class="top"></b><b></b></div>
<div class="layer"><i></i><i></i><i></i><i></i><b></b><b></b></div>
<div class="layer"><i></i><i></i><i></i><i></i><b></b><b class="bottom"></b></div>
</div>
</div>
.scene {
/* one base unit: every length below is a multiple of it, so the cube is the same share of a
card, the editor, a full screen and a recording canvas */
--u: 0.33vmin;
perspective: calc(800 * var(--u));
}
.rubik {
position: relative;
width: calc(120 * var(--u));
height: calc(120 * var(--u));
transform-style: preserve-3d;
transform: rotateX(-28deg) rotateY(-38deg);
}
/* one layer = one flat box, 120 x 40 x 120 */
.layer {
position: absolute;
left: 0;
width: calc(120 * var(--u));
height: calc(40 * var(--u));
transform-style: preserve-3d;
animation: twist 12s cubic-bezier(0.65, 0, 0.35, 1) infinite;
}
.layer:nth-child(1) { top: 0; }
.layer:nth-child(2) { top: calc(40 * var(--u)); animation-delay: -11s; } /* one second later */
.layer:nth-child(3) { top: calc(80 * var(--u)); animation-delay: -10s; } /* two seconds later */
/* stickers: black lines on the tile edges, 3 across and --row down */
.layer > * {
position: absolute;
left: 0;
background-color: var(--c, #10121f);
background-image:
linear-gradient(90deg, #10121f calc(3 * var(--u)), transparent calc(3 * var(--u)) calc(100% - 3 * var(--u)), #10121f calc(100% - 3 * var(--u))),
linear-gradient(#10121f calc(3 * var(--u)), transparent calc(3 * var(--u)) calc(100% - 3 * var(--u)), #10121f calc(100% - 3 * var(--u)));
background-size: 33.334% 100%, 100% var(--row, 100%);
backface-visibility: hidden;
}
/* four sides: strips of three stickers */
.layer i { top: 0; width: calc(120 * var(--u)); height: calc(40 * var(--u)); }
.layer i:nth-child(1) { --c: #8b6cff; transform: translateZ(calc(60 * var(--u))); }
.layer i:nth-child(2) { --c: #2ee6d6; transform: rotateY(90deg) translateZ(calc(60 * var(--u))); }
.layer i:nth-child(3) { --c: #ff4d9d; transform: rotateY(180deg) translateZ(calc(60 * var(--u))); }
.layer i:nth-child(4) { --c: #ffb547; transform: rotateY(-90deg) translateZ(calc(60 * var(--u))); }
/* top and bottom of a layer: black inside the cube, stickers only on the outside */
.layer b {
--row: 33.334%;
top: calc(-40 * var(--u));
width: calc(120 * var(--u));
height: calc(120 * var(--u));
transform: rotateX(90deg) translateZ(calc(20 * var(--u)));
}
.layer b + b {
transform: rotateX(-90deg) translateZ(calc(20 * var(--u)));
}
.layer .top { --c: #f2f3ff; }
.layer .bottom { --c: #4d8dff; }
/* a quarter turn in the first 6% of each quarter, then hold */
@keyframes twist {
0% { transform: rotateY(0deg); }
6%, 25% { transform: rotateY(90deg); }
31%, 50% { transform: rotateY(180deg); }
56%, 75% { transform: rotateY(270deg); }
81%, 100% { transform: rotateY(360deg); }
}