GODRICH
#css-animation#button#loader#transition#scroll#dashboard#ui#design-trends

HTML CSS JS mini games: 9 copy-paste mini apps

HTML CSS JS mini games are small browser toys and work tools built from plain markup.

Auto-plays · click a tile to jump to its section · all nine in one zip

The order is not a popularity ranking; it follows how much work each screen asks of the person in front of it. Picking one shape out of four (01) is the lightest, then typing digits into a value (02), letting the page watch the clock for you (03), shaking six cards out of their seats (04), pulling design values out of sliders (05), timing down to a hundredth and keeping the record (06), judging a single sentence true or false (07), and the last two decide an outcome on your behalf (08, 09). Item 05, which spits out values and code together, belongs to the same family as the 9 interactive CSS generators, and 03 carves its disc with the same angle trick the circular progress bar roundup used, turned toward a timer. Each item is one folder holding html, css and js, and none of them asks for a build tool.

01Logo match puzzle

A quiz board that shows a geometric mark on a display card and asks you to pick the same shape from four options — a natural ice-breaker for the first screen of an event page or a learning quiz.

Logo match puzzle
Open in a new tab

01 · Logo match puzzle — Measured area 17.351% · moving frames 19/23Original by GODRICH · SCSS and React ship in the zip · See the password

clip-pathconic-gradientaria-live

The six marks are CSS shapes, not image files

.lp__mark[data-shape="hex"] { clip-path: polygon(50% 0, 94% 25%, 94% 75%, 50% 100%, 6% 75%, 6% 25%); }
.lp__mark[data-shape="diamond"] { clip-path: polygon(50% 2%, 98% 50%, 50% 98%, 2% 50%); }
.lp__mark[data-shape="tri"] { clip-path: polygon(50% 4%, 96% 92%, 4% 92%); }
.lp__mark[data-shape="chevron"] { clip-path: polygon(16% 0, 100% 50%, 16% 100%, 0 100%, 84% 50%, 0 0); }
.lp__mark[data-shape="wedge"] {
  background-color: transparent; border-radius: 50%;
  background-image: conic-gradient(from 205deg, currentColor 0deg 125deg, transparent 125deg 360deg);
}

Hexagon, diamond, triangle and chevron come from clip-path coordinates and the wedge is one conic-gradient line, so the item loads zero images, and 19 of the 23 frame-to-frame comparisons in the grid preview showed movement.

A wrong pick lights the right tile instead

  function pick(i) {
    if (lock || !doneEl.hidden) { return; }
    lock = true;
    root.classList.remove('is-demo');
    var answer = QUESTIONS[qIndex].answer;
    var ok = tiles[i].firstElementChild.getAttribute('data-shape') === answer;
    tiles[i].classList.add(ok ? 'is-correct' : 'is-wrong');
    if (ok) { score++; scoreEl.textContent = String(score); }
    else {
      for (var k = 0; k < tiles.length; k++) {
        if (tiles[k].firstElementChild.getAttribute('data-shape') === answer) { tiles[k].classList.add('is-correct'); }
      }
    }
    window.setTimeout(next, NEXT_MS);
  }

A correct pick adds is-correct to the tile you chose, while a wrong one adds is-wrong and then finds the tile holding the answer shape and lights that one too; either way the board waits 0.8s before moving to the next question.

02Keypad calculator

An arithmetic keypad where keyboard digits, operators, Enter and Backspace work exactly as printed — the base shape of a calculator for screens that tally quotes or add up order counts.

Keypad calculator
Open in a new tab

02 · Keypad calculator — Measured area 3.732% · moving frames 17/23Original by GODRICH · SCSS and React ship in the zip · See the password

grid-template-areasfont-variant-numericuser-select

Twenty keys placed by one area map

.kc__pad {
  position: relative; flex: 1 1 auto; min-height: 0;
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  grid-template-rows: repeat(5, 1fr);
  grid-template-areas:
    "clear back pct div"
    "n7 n8 n9 mul"
    "n4 n5 n6 sub"
    "n1 n2 n3 add"
    "n0 n0 dot eq";
  gap: $sp-2;
  --kc-space: #{$sp-2};
}

The 4×5 grid draws its area map first and each key claims its seat with grid-area, so the layout where only the 0 key spans two cells falls out with no coordinate math.

Chained operators keep only the last one

  function operator(o) {
    var v = Number(shown);
    if (op !== null && !fresh) {
      var r = calc(acc, v, op);
      if (r === null) { fail(); return; }
      acc = r; show(fmt(r));
    } else { acc = v; }
    op = o; fresh = true;
  }

When an operator is already waiting and a new number has arrived, it calculates first and stacks the result into the accumulator; otherwise it simply stores the new value, so pressing × after + keeps only the later operator.

03Pomodoro ring timer

A pomodoro timer html css js build that survives a refresh — 25 minutes of focus and a 5-minute break turning on one ring, ready for a personal work timer or a class progress board.

Pomodoro ring timer
Open in a new tab

03 · Pomodoro ring timer — Measured area 7.381% · moving frames 22/23Original by GODRICH · SCSS and React ship in the zip · See the password

conic-gradientanimation-play-statearia-valuenow

One angle variable empties the disc

.pt__disc {
  position: absolute; inset: 0; border-radius: 50%;
  --pt-angle: 360deg;
  --pt-fill: #{$color};
  background-image: conic-gradient(var(--pt-fill) 0deg var(--pt-angle), rgba($subject-ink, .18) var(--pt-angle) 360deg);
  animation-name: ptDrain;
  animation-duration: 25s;                // JS rewrites this as phase length / SPEED
  animation-timing-function: linear;      // a real timer must run at constant speed
  animation-iteration-count: 1;
  animation-fill-mode: both;
  animation-play-state: paused;
}
.pt.is-running .pt__disc { animation-play-state: running; }
.pt.is-break .pt__disc { --pt-fill: #{$subject-blue-deep}; }
@keyframes ptDrain {
  from { --pt-angle: 360deg; }
  to { --pt-angle: 0deg; }
}

Because --pt-angle is registered with @property as an <angle>, the stretch between keyframes interpolates and pausing or resuming is one animation-play-state line, so JS never recomputes the clock face; the demo runs with SPEED = 60, which burns a minute of the timer every real second and ends the 25-minute phase in 25 seconds, so set that constant back to 1 for real use, and the left-over seconds it stores come back through the Web Storage API below.

Left-over time that survives a refresh

  try {
    var saved = JSON.parse(window.localStorage.getItem(KEY) || 'null');
    if (saved && typeof saved.left === 'number') {
      root.classList.remove('is-demo');
      phase = saved.phase === 'break' ? 'break' : 'focus';
      left = Math.min(Math.max(saved.left, 0), total());
      syncDisc();
      run(!!saved.running);
    }
  } catch (e) { /* ignore a broken value and start fresh */ }

The remaining seconds and the current phase come back when the window reopens, and a corrupt stored value quietly resets the timer to its first state.

04Card shuffle and sort

A display rail of six number cards that trade seats in place when you press shuffle and walk back into order when you press sort — for drawing turn order or shaking up team assignments.

Card shuffle and sort
Open in a new tab

04 · Card shuffle and sort — Measured area 38.397% · moving frames 22/23Original by GODRICH · SCSS and React ship in the zip · See the password

transitiontranslatepointer-events

The DOM order of the cards never changes

.cs__card {
  position: absolute; left: 0; bottom: 0;
  width: calc((100% - #{$sp-2} * 5) / 6); aspect-ratio: 3 / 4;
  --step: calc(100% + #{$sp-2});
  --slot: 0;
  --slotAlt: 0;
  --lift: 0%;
  translate: calc(var(--step) * var(--slot)) 0;
  transition: translate $dur-enter $easing;
  display: grid; place-items: center;
  border-radius: $r-control;
  background-color: $color; color: $subject-cream;   // deep-blue face + cream text 5.98:1
  box-shadow: $shadow-press;
  font-weight: 800; font-size: clamp(14px, 4vw, 22px);
  user-select: none;
}

One slot number --slot decides the seat and one step equals the card's own width plus the gap, so whenever the numbers change, transition carries the card across the distance.

Buttons lock while the cards move

  function shuffle() {
    if (moving) { return; }
    root.classList.remove('is-demo');
    var before = slots.join('');
    for (var i = slots.length - 1; i > 0; i--) {            // Fisher-Yates
      var j = Math.floor(Math.random() * (i + 1));
      var t = slots[i]; slots[i] = slots[j]; slots[j] = t;
    }
    if (slots.join('') === before) { var h = slots[0]; slots[0] = slots[5]; slots[5] = h; }
    paint(); lock();
    stateEl.textContent = SHUFFLED;
  }

The slots go through a Fisher-Yates shuffle, and when the result equals the previous order the two ends swap once more so the order always changes; for the 0.6s of motion .is-moving turns off pointer events on the buttons, so rapid clicks cannot tangle the seats, and sort walks every card back to its starting seat through the same transition.

05Glass effect generator

A tool whose blur, opacity, saturation and border sliders move the preview card and the code lines underneath at the same time — for pinning glass card values without writing CSS by hand.

Glass effect generator
Open in a new tab

05 · Glass effect generator — Measured area 17.794% · moving frames 22/23Original by GODRICH · SCSS and React ship in the zip · See the password

backdrop-filterhslclamp

One function is the only place values are made

  function code() {
    return [
      'backdrop-filter: blur(' + ranges.blur.value + 'px) saturate(' + ranges.saturate.value + '%);',
      'background: hsl(0 0% 100% / ' + alpha() + ');',
      'border: ' + ranges.border.value + 'px solid hsl(0 0% 100% / .5);'
    ];
  }
  function render() {
    var rows = code();
    glass.style.backdropFilter = 'blur(' + ranges.blur.value + 'px) saturate(' + ranges.saturate.value + '%)';
    glass.style.backgroundColor = 'hsl(0 0% 100% / ' + alpha() + ')';
    glass.style.borderWidth = ranges.border.value + 'px';
    outs.blur.textContent = ranges.blur.value + 'px';
    outs.opacity.textContent = ranges.opacity.value + '%';
    outs.saturate.textContent = ranges.saturate.value + '%';
    outs.border.textContent = ranges.border.value + 'px';
    for (var i = 0; i < lines.length; i++) { lines[i].textContent = rows[i]; }
  }

The three lines code() returns are written into the card's inline style and into the code box in the same pass, so the value you copy and the card you look at can never disagree.

backdrop-filter needs a sharp backdrop behind it

.gg__scene { position: relative; overflow: hidden; border-radius: $r-card; background-color: rgba($subject-cream, .06); min-height: 120px; }
// over-blur the blobs and the card's backdrop-filter has nothing left to show — keep them less blurred
.gg__blob { position: absolute; width: 64%; aspect-ratio: 1; border-radius: 50%; filter: blur(9px); }
.gg__blob--a { left: -12%; top: -10%; background-color: $subject-pink; }
.gg__blob--b { right: -14%; top: 18%; background-color: $subject-mint; }
.gg__blob--c { left: 18%; bottom: -18%; background-color: $subject-sky; }
.gg__glass {
  position: absolute; inset: 22% 12%;
  border-radius: $r-card;
  border-style: solid; border-width: 1px; border-color: hsl(0 0% 100% / .5);
  background-color: hsl(0 0% 100% / .18);
  backdrop-filter: blur(12px) saturate(160%);
  -webkit-backdrop-filter: blur(12px) saturate(160%);
}

The color blobs behind the card sit at only 9px of blur for exactly this reason: blur the background too far in advance and the work that backdrop-filter does disappears from the screen.

06Lap stopwatch

A stopwatch that runs in hundredths — space starts and stops it, the L key stamps a lap — for workout logs or timing talks at a seminar.

Lap stopwatch
Open in a new tab

06 · Lap stopwatch — Measured area 20.363% · moving frames 21/23Original by GODRICH · SCSS and React ship in the zip · See the password

font-variant-numericrequestAnimationFramearia-keyshortcuts

Digits that change without jittering

.sw__time {
  margin: 0;
  font-weight: 800; font-size: clamp(28px, 9vw, 44px); line-height: 1;
  font-variant-numeric: tabular-nums;
  color: $subject-ink;
  letter-spacing: -.02em;
}
.sw__sep { color: rgba($subject-ink, .72); }
.sw__cs { position: relative; display: inline-block; }

font-variant-numeric: tabular-nums pins every digit to one width, so the moment 09 rolls over into 10 the seconds column does not slide sideways.

One clock stream, one lap row at a time

  function tick() {
    if (!running) { return; }
    elapsed = base + (Date.now() - startAt);
    paint();
    window.requestAnimationFrame(tick);
  }
  function toggle() {
    root.classList.remove('is-demo');
    running = !running;
    if (running) { base = elapsed; startAt = Date.now(); window.requestAnimationFrame(tick); } else { save(); }
    paint();
  }
  function lap() {
    root.classList.remove('is-demo');
    count++;
    laps.unshift({ n: count, at: fmt(elapsed) });
    laps = laps.slice(0, MAX);
    listEl.insertBefore(row(laps[0], true), listEl.firstChild);
    while (listEl.children.length > MAX) { listEl.removeChild(listEl.lastChild); }
    save();
  }

requestAnimationFrame reprints the elapsed time in step with the display's refresh rate, and a lap inserts one row at the top of the list instead of redrawing it — only the arriving row slides in.

07OX quiz panel

One statement at a time, answered with the O/X buttons or the O and X keys — a hit lights blue, a miss lights red — for live-session quizzes and lecture slides.

OX quiz panel
Open in a new tab

07 · OX quiz panel — Measured area 14.505% · moving frames 15/23Original by GODRICH · SCSS and React ship in the zip · See the password

aria-pressedtransitioncounter-increment

Only a miss adds a hint outline to the right button

  function pick(v) {
    if (lock) { return; }
    root.classList.remove('is-demo');
    if (step >= QUESTIONS.length) { reset(); return; }
    lock = true;
    var ok = QUESTIONS[step].a === v;
    var picked = v === 'O' ? oBtn : xBtn;
    var right = QUESTIONS[step].a === 'O' ? oBtn : xBtn;
    picked.setAttribute('aria-pressed', 'true');
    picked.classList.add(ok ? 'is-hit' : 'is-miss');
    if (!ok) { right.classList.add('is-hint'); }
    if (ok) { score++; scoreEl.textContent = String(score); }
    dots[step].classList.add(ok ? 'is-hit' : 'is-miss');
    msgEl.textContent = ok ? HIT : MISS;

The pressed button gets aria-pressed and a fill color that matches the verdict, and only on a miss does the correct button gain an inner outline; because the color changes land as hard cuts, 15 of the 23 comparisons showed movement.

CSS counts the progress dots

.ox__dots {
  display: flex; justify-content: center; gap: $sp-2;
  margin: 0; padding: 0; list-style: none;
  counter-reset: oxStep;
}
.ox__dot {
  counter-increment: oxStep;
  display: grid; place-items: center;
  width: clamp(16px, 5vw, 22px); aspect-ratio: 1;
  border-radius: 50%;
  background-color: $subject-cream; color: $subject-ink;
  font-weight: 700; font-size: clamp(8px, 2.2vw, 10px); line-height: 1;
}
.ox__dot::before { content: counter(oxStep); }
.ox__dot.is-now { outline: 2px solid $subject-ink; outline-offset: 2px; }
.ox__dot.is-hit { background-color: $color; color: $subject-cream; }
.ox__dot.is-miss { background-color: $error; color: $subject-cream; }

A counter started with counter-reset advances once per dot and numbers them 1 through 5, so JS touches only the colors and leaves the digits alone.

08Conic roulette spin

A six-slice wheel that slows through a long tail and calls out the wedge the pointer stops on — for prize drawings at events and picking presentation order.

Conic roulette spin
Open in a new tab

08 · Conic roulette spin — Measured area 21.038% · moving frames 16/23Original by GODRICH · SCSS and React ship in the zip · See the password

conic-gradienttransformtransition-timing-function

The disc is one conic-gradient

.rl__disc {
  // The label seat is a disc-sized box rotated by the slice angle, so its rotated bounds outgrow the disc —
  // that overflow reached body.scrollHeight and broke 320px BD9 (207px measured); clip inside the circle
  position: absolute; inset: 0; border-radius: 50%; overflow: hidden;
  box-shadow: 0 0 0 4px $subject-ink;
  // 6 slices + 1deg ink boundary lines. Hard stops, so the colors never blend
  background-image: conic-gradient(
    $subject-ink 0deg 1deg,     $subject-blue-deep 1deg 60deg,
    $subject-ink 60deg 61deg,   $stage-yellow 61deg 120deg,
    $subject-ink 120deg 121deg, $stage-orange 121deg 180deg,
    $subject-ink 180deg 181deg, $subject-lilac 181deg 240deg,
    $subject-ink 240deg 241deg, $subject-cream 241deg 300deg,
    $subject-ink 300deg 301deg, $subject-ink 301deg 360deg
  );
  transform: rotate(var(--spin));
  transition-property: transform;
  transition-duration: 2400ms;
  transition-timing-function: $spin-ease;
}

Slice colors and the 1-degree ink boundaries are drawn together as conic-gradient hard stops, and the long tail of the spin comes from the dedicated curve sitting in transition-timing-function.

Pick the slice first, then build the angle

  function spin() {
    if (busy) { return; }
    root.classList.remove('is-demo');
    busy = true;
    btn.disabled = true;
    out.textContent = SPINNING;
    var turns = 5 + Math.floor(Math.random() * 4);                       // 5-8 turns
    var idx = Math.floor(Math.random() * PRIZES.length);                 // the slice to stop on
    var target = (360 - (idx * SLICE + SLICE / 2) + 360) % 360;          // angle putting that slice's center at 12 o'clock
    angle += turns * 360 + ((target - (angle % 360)) + 360) % 360;
    root.style.setProperty('--spin', angle + 'deg');
  }

The slice is drawn first, then the angle that brings its center to 12 o'clock is computed and five to eight full turns are stacked on top, so the outcome is decided before the wheel moves and the screen only follows the number.

09Random slot picker

A picker whose name reel streams past and stops on the winner in the center band, with eight names included as the default list — for comment-event drawings and choosing who presents.

Random slot picker
Open in a new tab

09 · Random slot picker — Measured area 2.549% · moving frames 9/23Original by GODRICH · SCSS and React ship in the zip · See the password

animation-timing-functionstepsaria-live

The window height is a whole multiple of row height

.sl__window {
  position: relative; flex: 0 0 auto;
  width: clamp(104px, 34vw, 160px); height: calc(var(--row) * 3);   // window height is a whole multiple of row height
  overflow: hidden; border-radius: $r-card;
  background-color: $subject-cream;
}
// During a real draw the row can straddle the window border — fade top and bottom to cream so no name ends half-clipped
.sl__window::before,
.sl__window::after {
  content: ""; position: absolute; left: 0; right: 0; z-index: 1;
  height: 14%; pointer-events: none;
}
.sl__window::before { top: 0; background-image: linear-gradient($subject-cream, rgba($subject-cream, 0)); }
.sl__window::after { bottom: 0; background-image: linear-gradient(rgba($subject-cream, 0), $subject-cream); }

One row height --row sets the window height, the highlight band and the travel distance together, while the gradients at the top and bottom cover names that straddle the border.

The stop position rewinds by remainder

  track.addEventListener('transitionend', function (e) {
    if (e.propertyName !== 'transform') { return; }
    offset = offset % list.length;
    winner = list[(offset + 1) % list.length];
    track.style.transitionProperty = 'none';
    put(offset);
    void track.offsetHeight;                    // force one reflow so the rewind does not animate
    track.style.transitionProperty = 'transform';
    banner.textContent = WIN.replace('{n}', winner);
    banner.classList.add('is-win');
    busy = false;
    goBtn.disabled = false;
  });

After the stop, the position rewinds by the remainder over the candidate count with one forced reflow in between, so the track never runs off the end and the rewind never shows, and the four stacked copies of the list keep the seam out of sight.

Where do css mini games like these fit on a real page?

The files run as-is the moment you open them, but dropping one onto your own page comes down to three known edits. The first is data: the five questions in 01, the five statements in 07 and the eight names in 09 sit in plain constants at the top of their files, so swapping the array is the whole job, and since 01 walks its QUESTIONS array in fixed order, its New question button starts the same five over instead of drawing at random. The second is the storage key — 03, 06 and 09 write under pomodoro080, p080-06-stopwatch and p080-09, and putting two of them on one page means splitting the key names so they stop overwriting each other's records. The third is the is-demo class: it exists only to drive the preview loop in the grid, the script strips it the first time a person touches the item, and you can leave it out entirely when embedding for real. And if what you actually came for is arithmetic, most javascript calculator code copy and paste snippets stop at the four operations, while 02 already ships the keypad layout, the fixed-width digits and the full keyboard wiring, so the paste is the entire feature.

Where it breaks — pitfalls

In the grid the nine run as CSS clocks and under a human hand they run through JS, but what actually stood in the way of this piece was neither of those — it was rotation and integers. Every item below is a thing that broke on screen during measurement and was then fixed in the source you see above, not a hypothetical warning.

Spun labels stand upside down and their box outgrows the disc

08 first rotated each label as a whole, by the slice angle. The upper slices read fine, but the labels of the lower slices stood upside down and went unreadable in the list thumbnail. Splitting the label's seat box from its text box and giving the text rotate(calc(var(--a) * -1 - var(--spin))) keeps every label horizontal at any angle. In the same item the rotated label boxes grew past the disc and pushed body.scrollHeight 207px over the limit at a 320px width — a check of element rects never caught it — until overflow: hidden on the disc clipped everything back inside the circle. The pointer went through a decision of its own: it stands outside the disc, on the stage background, with only its tip reaching the border, because an ink arrow resting on the disc disappears whenever the wheel stops on the ink or deep-blue slice.

A reel that misses the integer grid clips names

The reel of 09 started with a window height that did not divide evenly into row heights, so every stopped frame caught a name half-clipped at the top and bottom. Switching the climb to steps(var(--rows), end), one whole row at a time, made every frame land on a whole row inside the window, and the password that opens the archive holding all nine is eu89k2wk. The cumulative area of 2.55% in this item is the smallest of the nine precisely because the reel advances in cuts.

Swapping seats in place stacks the cards

The cards of 04 overlapped mid-flight in the first cut, and in the poster frame one card sat completely hidden behind another. Reworking the preview into a rotation where the whole row advances one seat together and only the last card lifts over the top to cross to the front keeps all six cards visible in every frame. The rail itself is 1.85 times taller than a card so that the one crossing card lifts and travels inside the stage rather than being clipped at the 320px edge, and the crossing card is also the last to land, dropping into a seat only after the front card has vacated it. Because six cards travel as a group, its cumulative area of 38.40% is the largest of the nine.

Three tweaks and accessibility

Three values are all it takes to change the character of all nine at once.

Name Changed value Feel
duration $dur-loop 2s → 1s shuffle, roulette and reel run twice as fast
easing $ease-out → $ease-pop cards and chips bounce one more time
color $subject-blue → $stage-yellow the blue lead turns yellow and the stage brightens

Keys come ahead of the mouse here. Item 01 answers on the number keys 1 to 4, 02 takes digits and operators with Enter, Backspace and Escape, 04 listens for S and R, 06 for the space bar with L and R, and 07 reads the O and X keys directly; which key a button answers to is written on the button itself in aria-keyshortcuts. Building the on-screen keycap row and help sheet is its own job, collected in 9 keyboard shortcut UI patterns. Scores and result lines are announced through aria-live and role="status", and the disc of 03 reports the remaining share as a number in aria-valuenow on a role="progressbar" element, where it starts at 100 and moves down as the ring drains. Under prefers-reduced-motion: reduce all nine stop their grid preview loops and the slide of the cards, the reel and the roulette disc ends instantly, leaving only the result screens, while the disc of 03 is not carved at all and the remaining time counts down in digits alone. Text contrast was re-measured on rendered pixels at 52 spots across the nine screens, with zero failures under AA.

FAQ

Do css mini games like these need a canvas

All nine are drawn entirely with DOM elements and CSS, and a canvas never appears. Canvas earns its keep on screens that must repaint pixels every frame, while screens with a fixed count of cells, cards and discs get more out of the DOM in accessibility and in clean copy-paste. The progress dots of 07 even count themselves with a CSS counter, so the numbers you see are typeset, not scripted.

Can these go straight into a Tistory or WordPress post

Pasting an item's html, css and js verbatim usually just works, but some post editors strip style or script blocks, so wrapping the item in an iframe is the safer route. Class names carry a two-letter prefix per item, so they do not collide with the CSS of your blog skin. The nine also hold up at a 320px width — every item was re-measured there with zero overflow — so a phone-width embed will not break your layout.

How do I clear the saved times or candidate lists

Items 03, 06 and 09 keep their values in the browser's localStorage, so deleting the matching key returns that item to its first state. The Restore list button inside 09 does the same job on screen, and the Reset button of 06 empties the clock and the laps together. Removing the drawn winner in 09 shrinks the candidate count on screen and the shorter list is what gets saved, so the pool keeps shrinking across rounds of a drawing until you restore it.

Enter the archive password

The password is inside this article. You will find it as you read.