Files
random_tools/checkpoints/dice-20260808-audit-pass-v1/dice.html
T
2026-08-11 09:53:42 -04:00

168 lines
4.5 KiB
HTML

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Side Cards — Dice</title>
<style>
* { box-sizing: border-box; margin: 0; padding: 0; }
body {
min-height: 100vh;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 36px;
padding: 32px;
color: #fff;
background: radial-gradient(ellipse at center, #0b6b3a 0%, #074d28 100%);
font-family: Georgia, "Times New Roman", serif;
}
h1 {
font-size: 26px;
letter-spacing: 1px;
text-shadow: 0 2px 6px rgba(0,0,0,.5);
}
.dice-stage {
position: relative;
width: min(540px, calc(100vw - 32px));
aspect-ratio: 54 / 35;
}
#dice-canvas {
display: block;
width: 100%;
height: 100%;
outline: none;
}
.loading {
position: absolute;
inset: 0;
display: grid;
place-items: center;
color: rgba(255,255,255,.65);
font-size: 11px;
letter-spacing: 2px;
text-transform: uppercase;
pointer-events: none;
transition: opacity .18s;
}
.dice-stage.ready .loading { opacity: 0; }
.readout {
min-height: 62px;
display: flex;
flex-direction: column;
gap: 8px;
text-align: center;
}
.total { font-size: 34px; letter-spacing: 2px; }
.detail { font-size: 13px; letter-spacing: 3px; text-transform: uppercase; opacity: .7; }
.tally { font-size: 11px; letter-spacing: 1.5px; opacity: .55; font-variant-numeric: tabular-nums; }
.controls { display: flex; gap: 12px; }
.controls button {
padding: 11px 24px;
border: 1px solid rgba(255,255,255,.35);
border-radius: 8px;
color: #fff;
background: rgba(255,255,255,.12);
font: inherit;
font-size: 14px;
letter-spacing: 1px;
cursor: pointer;
transition: background .15s, transform .1s;
}
.controls button:hover { background: rgba(255,255,255,.22); }
.controls button:active { transform: translateY(1px); }
.controls button[disabled] { opacity: .45; cursor: default; transform: none; }
</style>
</head>
<body>
<h1>Dice</h1>
<div class="dice-stage" id="stage">
<canvas id="dice-canvas" aria-label="Two red dice"></canvas>
<div class="loading" id="loading">loading dice</div>
</div>
<div class="readout">
<div class="total" id="total">&mdash;</div>
<div class="detail" id="detail">ready</div>
<div class="tally" id="tally"></div>
</div>
<div class="controls">
<button id="throw" disabled>Throw</button>
<button id="reset">Reset Tally</button>
</div>
<script src="dice-renderer.bundle.js"></script>
<script>
const stage = document.getElementById("stage");
const canvas = document.getElementById("dice-canvas");
const total = document.getElementById("total");
const detail = document.getElementById("detail");
const tallyText = document.getElementById("tally");
const throwButton = document.getElementById("throw");
const tally = new Array(7).fill(0);
let rolls = 0;
let tray;
function updateTally(values) {
values.forEach((value) => tally[value]++);
rolls += values.length;
const parts = [];
for (let face = 1; face <= 6; face++) {
parts.push(`${face}: ${(100 * tally[face] / rolls).toFixed(1)}%`);
}
tallyText.textContent = `${rolls} faces — ${parts.join(" ")}`;
}
async function throwDice() {
if (!tray || tray.isBusy()) return;
throwButton.disabled = true;
detail.textContent = "rolling";
total.textContent = "··";
const values = await tray.roll();
if (values) {
total.textContent = values[0] + values[1];
detail.textContent = `${values[0]} + ${values[1]}`;
updateTally(values);
}
throwButton.disabled = false;
}
Dice3D.mount(canvas, 2).then((mounted) => {
tray = mounted;
stage.classList.add("ready");
throwButton.disabled = false;
}).catch((error) => {
console.error(error);
document.getElementById("loading").textContent = "3D dice unavailable";
detail.textContent = "renderer error";
});
throwButton.addEventListener("click", throwDice);
document.getElementById("reset").addEventListener("click", () => {
tally.fill(0);
rolls = 0;
tallyText.textContent = "";
});
window.addEventListener("keydown", (event) => {
if (event.code === "Space" || event.code === "Enter") {
event.preventDefault();
throwDice();
}
});
</script>
</body>
</html>