DEV Community

Cover image for How I Built a Dice Roller That Google Can't Beat — A Technical Deep Dive
Zihang Dong 董子航
Zihang Dong 董子航

Posted on

How I Built a Dice Roller That Google Can't Beat — A Technical Deep Dive

Google's built-in dice roller was stealing my search traffic. Instead of competing, I built something it can't replicate: batch rolling, game presets, probability visualization, CSV export, and 3D CSS dice faces. Here's the full technical breakdown.

How I Built a Dice Roller That Google Can't Beat

Google was stealing my clicks. Every time someone searched "dice roller," Google's own interactive dice widget sat at the top of the results — a fully functional, zero-click tool that let users roll dice without ever visiting a website.

This is the story of how I analyzed the competitive landscape, identified what Google's built-in tool couldn't do, and built a dice roller that serves real users instead of competing with a search giant head-on.


Part 1: The Discovery

I run ToolKnit, a collection of 90 free browser-based tools. Our dice roller had been live for months — it supported D4 through D100, custom sides, multi-dice rolls, and had a clean dark UI. But the click-through rate from Google Search was abysmal.

When I looked at the SERP for "dice roller," the answer was obvious:

Google SERP for dice roller

Google displays an interactive dice widget directly in the search results. Users can click it, roll dice, and get results — all without leaving Google. This is a SERP Feature specifically designed for dice-related queries, and it cannibalizes organic traffic almost completely.

The conventional SEO playbook says: "Optimize your meta tags, build more backlinks, write better content." But none of that matters when Google puts a working tool above your link. Users don't scroll down because they don't need to.

The insight: Stop trying to outrank Google. Instead, build something Google's widget can't do.


Part 2: What Google's Dice Can't Do

Google's built-in dice roller is minimal by design. It supports:

  • D4, D6, D8, D10, D12, D20, D100
  • Multiple dice (e.g., 3d6)
  • Adding modifiers (+3, +5)
  • An animation that shows results

What it doesn't support:

  • ❌ Game-specific presets (Monopoly, Yahtzee, Catan, Craps)
  • ❌ Batch rolling (10×, 50×, 100×)
  • ❌ Roll history with CSV export
  • ❌ Probability distribution visualization
  • ❌ Dice face customization (skins)
  • ❌ Keyboard shortcuts
  • ❌ Sound effects
  • ❌ Session statistics (total rolls, average, grand sum)

Every feature on this list is something a real user would want — whether they're a D&D player tracking combat rounds, a teacher running a probability lesson, or a board game enthusiast who lost their physical dice.

I decided to build all of them.


Part 3: The Technical Build

Here's the architecture: zero dependencies, pure vanilla JavaScript, CSS animations, and HTML5 Canvas. No npm, no build step, no framework. The tool is a single HTML file with inline JS that loads in under 300ms.

3.1 3D Dice Faces with Pure CSS

The dice aren't images or SVGs — they're styled <div> elements with CSS grid layouts. Each face shows the correct dot pattern:

.dice-face {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  grid-template-rows: repeat(3, 1fr);
  width: 64px;
  height: 64px;
  background: #1a1a2e;
  border: 2px solid rgba(255,255,255,0.12);
  border-radius: 14px;
  padding: 8px;
}

.dot {
  width: 12px;
  height: 12px;
  background: #fff;
  border-radius: 50%;
  place-self: center;
}
Enter fullscreen mode Exit fullscreen mode

The roll animation applies a CSS transform sequence — rotation in 3D space with a scale bounce:

@keyframes diceRoll {
  0%   { transform: rotateX(0deg) rotateY(0deg) rotateZ(0deg) scale(1); }
  25%  { transform: rotateX(180deg) rotateY(90deg) rotateZ(45deg) scale(1.1); }
  50%  { transform: rotateX(360deg) rotateY(180deg) rotateZ(90deg) scale(0.95); }
  75%  { transform: rotateX(540deg) rotateY(270deg) rotateZ(135deg) scale(1.05); }
  100% { transform: rotateX(720deg) rotateY(360deg) rotateZ(180deg) scale(1); }
}
Enter fullscreen mode Exit fullscreen mode

Each die gets a different animation delay so they tumble sequentially, not simultaneously — this creates a much more satisfying visual cascade.

3.2 requestAnimationFrame for Batch Rolling

The batch roll feature was the hardest technical challenge. Rolling 100 dice at once with the full animation loop would freeze the browser. The solution: chunked processing with requestAnimationFrame.

function batchRoll(count) {
  if (isRolling) return;
  isRolling = true;

  var batchResults = [];
  var i = 0;

  function step() {
    // Process 10 rolls per frame to avoid UI freeze
    var chunk = Math.min(10, count - i);

    for (var j = 0; j < chunk; j++) {
      var results = [];
      for (var d = 0; d < diceCount; d++) {
        results.push(Math.floor(Math.random() * 6) + 1);
      }
      batchResults.push(results);

      var total = results.reduce(function(a, b) { return a + b; }, 0);
      rolls++;
      grandTotal += total;
      results.forEach(function(v) { distribution[v]++; });
    }

    i += chunk;

    // Update UI every frame
    updateDistribution();
    updateHistory();
    progressBar.style.width = Math.round((i / count) * 100) + '%';

    if (i < count) {
      requestAnimationFrame(step);
    } else {
      // Final animation for the last result
      renderDice(batchResults[batchResults.length - 1]);
      isRolling = false;
    }
  }

  requestAnimationFrame(step);
}
Enter fullscreen mode Exit fullscreen mode

Key decisions:

  • 10 rolls per frame keeps the UI responsive while maintaining visual feedback
  • The progress bar updates every frame, giving users a satisfying sense of progress
  • The last roll gets the full dice animation treatment so it "feels" like a real roll
  • isRolling gate prevents double-clicks from stacking batch operations

3.3 Game Presets

Instead of asking users to configure dice manually, I added one-click presets for popular games:

Preset Dice Why
Monopoly 2d6 Classic property-buying rolls
Yahtzee 5d6 Five dice, three rolls per turn (manual)
Catan 2d6 Settlers resource distribution
Craps 2d6 Casino-style come-out roll
D&D Skill 1d20 Ability checks and saving throws

Each preset adjusts the dice count and type automatically. This small UX decision matters enormously for casual users who just want to play their game without reading documentation.

3.4 Probability Distribution with Theoretical Comparison

The distribution chart uses HTML5 Canvas to draw a bar chart showing the frequency of each die face (1-6). But the real insight was adding a theoretical overlay:

// Theoretical: exactly 1/6 = 16.67% per face
var theoretical = 1 / 6;
var theoreticalHeight = maxBarHeight * theoretical;
ctx.fillStyle = 'rgba(59, 130, 246, 0.25)';
ctx.fillRect(barX, chartH - theoreticalHeight, barW, theoreticalHeight);
Enter fullscreen mode Exit fullscreen mode

Each bar shows:

  1. Actual frequency (solid white/gray bar)
  2. Theoretical 16.67% (semi-transparent blue overlay)

As users roll more, the bars converge toward the theoretical line — visually demonstrating the Law of Large Numbers. This is useful for teachers and anyone curious about probability.

3.5 CSV Export

Roll history is more useful when you can take it elsewhere. The CSV export button generates a downloadable file with headers:

function exportCSV() {
  var csv = 'Roll#,Die1,Die2,Die3,Die4,Die5,Die6,Total\n';
  for (var i = historyData.length - 1; i >= 0; i--) {
    var row = historyData[i];
    csv += (row.roll) + ',' + row.results.join(',') + ',' + row.total + '\n';
  }
  var blob = new Blob([csv], { type: 'text/csv' });
  var url = URL.createObjectURL(blob);
  var a = document.createElement('a');
  a.href = url;
  a.download = 'dice-rolls.csv';
  a.click();
}
Enter fullscreen mode Exit fullscreen mode

This lets users analyze their rolls in Excel, Google Sheets, or any statistics tool. Combined with the batch roll feature, you can generate thousands of data points for a probability experiment in seconds.

3.6 Dice Skins

Three color themes (slate, indigo, emerald) that apply via CSS class switching. Under the hood, it's just swapping data-skin attributes and re-rendering. The DOM stays unchanged — only the visual presentation changes.

3.7 Keyboard Shortcuts

document.addEventListener('keydown', function(e) {
  if (e.key === ' ' || e.key === 'Enter') { roll(); }
  if (e.key === 'r' || e.key === 'R') { reset(); }
  if (e.key === 'ArrowUp') { changeDice(1); }
  if (e.key === 'ArrowDown') { changeDice(-1); }
  if (e.key === '1') { batchRoll(10); }
  if (e.key === '5') { batchRoll(50); }
  if (e.key === '0') { batchRoll(100); }
});
Enter fullscreen mode Exit fullscreen mode

Space/Enter to roll, R to reset, arrows to adjust dice count, number keys for batch operations. Power users love this.


Part 4: The Results

After the update, the dice roller went from a generic tool to a feature-rich dice toolbox. Key metrics:

Metric Before After
Weekly Google impressions ~200 2,230+
Weekly Bing impressions ~50 1,300+
Total weekly exposure ~250 3,500+

That's a 14× increase in three months.

The lesson: Google's SERP Features aren't an impenetrable wall. They're a constraint that forces you to build something genuinely better. If Google's widget does A, B, and C — build A through Z. Users will find you when they need more than what the search result page provides.


Part 5: What I'd Do Differently

  1. Web Workers for batch rolling. Currently, requestAnimationFrame is good enough for 100 rolls, but 1,000+ rolls should be offloaded to a Worker thread.
  2. IndexedDB for persistent roll history. Right now, history resets on page reload. Storing it locally would make the tool feel like a real app.
  3. Physics-based 3D dice. The CSS animations look good, but a Three.js or Canvas-based physics simulation would be the ultimate differentiator.
  4. Multiplayer sync. Imagine a shared dice room where multiple players see the same roll in real-time via WebSockets.

The Bigger Picture

This approach applies to any tool that faces Google SERP Feature competition: calculators, unit converters, timers, coin flippers, color pickers. The strategy is always the same:

  1. Analyze what the SERP Feature does.
  2. List everything it doesn't do that real users need.
  3. Build those things.

Don't try to outrank Google. Out-build them.


This article was written by the creator of ToolKnit — 90 free browser-based tools that run entirely in your browser with no sign-up and no uploads. Try the Dice Roller and let me know what else you'd want to see.

Top comments (0)