Skip to content

Instantly share code, notes, and snippets.

@bcardarella
Created October 17, 2025 19:30
Show Gist options
  • Save bcardarella/faa85965250279c3041a33e8ef905d80 to your computer and use it in GitHub Desktop.
Save bcardarella/faa85965250279c3041a33e8ef905d80 to your computer and use it in GitHub Desktop.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>DOM Benchmark Results</title>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/chart.umd.min.js"></script>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
min-height: 100vh;
padding: 20px;
}
.container {
max-width: 1400px;
margin: 0 auto;
}
header {
background: white;
padding: 30px;
border-radius: 10px;
box-shadow: 0 10px 30px rgba(0,0,0,0.2);
margin-bottom: 30px;
}
h1 {
color: #333;
font-size: 2.5em;
margin-bottom: 10px;
}
.subtitle {
color: #666;
font-size: 1.1em;
}
.implementations {
display: flex;
gap: 15px;
margin-top: 20px;
flex-wrap: wrap;
}
.impl-badge {
padding: 8px 16px;
border-radius: 20px;
font-size: 0.9em;
font-weight: 600;
display: flex;
align-items: center;
gap: 8px;
}
.impl-zig { background: #f9a825; color: white; }
.impl-chromium { background: #4285f4; color: white; }
.impl-firefox { background: #ff6611; color: white; }
.impl-webkit { background: #00aaff; color: white; }
.category {
background: white;
padding: 30px;
border-radius: 10px;
box-shadow: 0 10px 30px rgba(0,0,0,0.2);
margin-bottom: 30px;
}
.category h2 {
color: #333;
margin-bottom: 20px;
font-size: 1.8em;
}
.chart-container {
position: relative;
height: 400px;
margin-bottom: 30px;
}
.stats-table {
width: 100%;
border-collapse: collapse;
margin-top: 20px;
}
.stats-table th,
.stats-table td {
padding: 12px;
text-align: left;
border-bottom: 1px solid #eee;
}
.stats-table th {
background: #f5f5f5;
font-weight: 600;
color: #333;
}
.stats-table tr:hover {
background: #f9f9f9;
}
.winner {
font-weight: 600;
color: #4caf50;
}
.speedup {
font-size: 0.9em;
color: #666;
}
footer {
text-align: center;
color: white;
padding: 20px;
opacity: 0.8;
}
.legend {
display: flex;
gap: 20px;
margin-top: 15px;
flex-wrap: wrap;
}
.legend-item {
display: flex;
align-items: center;
gap: 8px;
}
.legend-color {
width: 20px;
height: 20px;
border-radius: 4px;
}
</style>
</head>
<body>
<div class="container">
<header>
<h1>🚀 DOM Benchmark Results</h1>
<p class="subtitle">Performance comparison: Zig vs Browser Implementations</p>
<div class="implementations">
<div class="impl-badge impl-zig">
<span>●</span> Zig
</div>
<div class="impl-badge impl-chromium">
<span>●</span> Chromium
</div>
<div class="impl-badge impl-firefox">
<span>●</span> Firefox
</div>
<div class="impl-badge impl-webkit">
<span>●</span> WebKit
</div>
</div>
</header>
<div class="category">
<h2>ID Queries</h2>
<div class="chart-container">
<canvas id="chart-0"></canvas>
</div>
<table class="stats-table">
<thead>
<tr>
<th>Benchmark</th>
<th>Zig</th><th>Chromium</th><th>Firefox</th><th>WebKit</th>
<th>Winner</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>getElementById: Small DOM (100)</strong></td>
<td>29µs<span class="speedup"> (2.1x slower)</span></td><td>14µs<span class="speedup"></span></td><td>26µs<span class="speedup"> (1.9x slower)</span></td><td>25µs<span class="speedup"> (1.8x slower)</span></td>
<td class="winner">Chromium</td>
</tr>
<tr>
<td><strong>getElementById: Medium DOM (1000)</strong></td>
<td>216µs<span class="speedup"> (1.5x slower)</span></td><td>143µs<span class="speedup"></span></td><td>274µs<span class="speedup"> (1.9x slower)</span></td><td>253µs<span class="speedup"> (1.8x slower)</span></td>
<td class="winner">Chromium</td>
</tr>
<tr>
<td><strong>getElementById: Large DOM (10000)</strong></td>
<td>11ms<span class="speedup"> (7.4x slower)</span></td><td>1ms<span class="speedup"></span></td><td>3ms<span class="speedup"> (1.9x slower)</span></td><td>3ms<span class="speedup"> (1.7x slower)</span></td>
<td class="winner">Chromium</td>
</tr>
<tr>
<td><strong>Pure query: getElementById (100 elem)</strong></td>
<td>5ns<span class="speedup"></span></td><td>93ns<span class="speedup"> (18.7x slower)</span></td><td>102ns<span class="speedup"> (20.4x slower)</span></td><td>77ns<span class="speedup"> (15.4x slower)</span></td>
<td class="winner">Zig</td>
</tr>
<tr>
<td><strong>Pure query: getElementById (1000 elem)</strong></td>
<td>5ns<span class="speedup"></span></td><td>93ns<span class="speedup"> (18.6x slower)</span></td><td>100ns<span class="speedup"> (20.0x slower)</span></td><td>72ns<span class="speedup"> (14.4x slower)</span></td>
<td class="winner">Zig</td>
</tr>
<tr>
<td><strong>Pure query: getElementById (10000 elem)</strong></td>
<td>4ns<span class="speedup"></span></td><td>97ns<span class="speedup"> (24.2x slower)</span></td><td>103ns<span class="speedup"> (25.7x slower)</span></td><td>71ns<span class="speedup"> (17.7x slower)</span></td>
<td class="winner">Zig</td>
</tr>
<tr>
<td><strong>Pure query: querySelector #id (100 elem)</strong></td>
<td>13ns<span class="speedup"></span></td><td>102ns<span class="speedup"> (7.8x slower)</span></td><td>170ns<span class="speedup"> (13.1x slower)</span></td><td>160ns<span class="speedup"> (12.3x slower)</span></td>
<td class="winner">Zig</td>
</tr>
<tr>
<td><strong>Pure query: querySelector #id (1000 elem)</strong></td>
<td>14ns<span class="speedup"></span></td><td>94ns<span class="speedup"> (6.7x slower)</span></td><td>170ns<span class="speedup"> (12.1x slower)</span></td><td>150ns<span class="speedup"> (10.7x slower)</span></td>
<td class="winner">Zig</td>
</tr>
<tr>
<td><strong>Pure query: querySelector #id (10000 elem)</strong></td>
<td>15ns<span class="speedup"></span></td><td>97ns<span class="speedup"> (6.5x slower)</span></td><td>170ns<span class="speedup"> (11.3x slower)</span></td><td>170ns<span class="speedup"> (11.3x slower)</span></td>
<td class="winner">Zig</td>
</tr>
</tbody>
</table>
</div>
<div class="category">
<h2>Tag Queries</h2>
<div class="chart-container">
<canvas id="chart-1"></canvas>
</div>
<table class="stats-table">
<thead>
<tr>
<th>Benchmark</th>
<th>Zig</th><th>Chromium</th><th>Firefox</th><th>WebKit</th>
<th>Winner</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>Pure query: getElementsByTagName (100 elem)</strong></td>
<td>5µs<span class="speedup"> (67.6x slower)</span></td><td>89ns<span class="speedup"> (1.2x slower)</span></td><td>121ns<span class="speedup"> (1.6x slower)</span></td><td>74ns<span class="speedup"></span></td>
<td class="winner">WebKit</td>
</tr>
<tr>
<td><strong>Pure query: getElementsByTagName (1000 elem)</strong></td>
<td>5µs<span class="speedup"> (68.5x slower)</span></td><td>86ns<span class="speedup"> (1.2x slower)</span></td><td>123ns<span class="speedup"> (1.7x slower)</span></td><td>73ns<span class="speedup"></span></td>
<td class="winner">WebKit</td>
</tr>
<tr>
<td><strong>Pure query: getElementsByTagName (10000 elem)</strong></td>
<td>7µs<span class="speedup"> (83.4x slower)</span></td><td>84ns<span class="speedup"></span></td><td>137ns<span class="speedup"> (1.6x slower)</span></td><td>130ns<span class="speedup"> (1.5x slower)</span></td>
<td class="winner">Chromium</td>
</tr>
<tr>
<td><strong>Pure query: querySelector tag (100 elem)</strong></td>
<td>15ns<span class="speedup"></span></td><td>206ns<span class="speedup"> (13.7x slower)</span></td><td>290ns<span class="speedup"> (19.3x slower)</span></td><td>240ns<span class="speedup"> (16.0x slower)</span></td>
<td class="winner">Zig</td>
</tr>
<tr>
<td><strong>Pure query: querySelector tag (1000 elem)</strong></td>
<td>15ns<span class="speedup"></span></td><td>2µs<span class="speedup"> (104.8x slower)</span></td><td>1µs<span class="speedup"> (71.3x slower)</span></td><td>1µs<span class="speedup"> (78.7x slower)</span></td>
<td class="winner">Zig</td>
</tr>
<tr>
<td><strong>Pure query: querySelector tag (10000 elem)</strong></td>
<td>16ns<span class="speedup"></span></td><td>16µs<span class="speedup"> (987.0x slower)</span></td><td>13µs<span class="speedup"> (815.6x slower)</span></td><td>20µs<span class="speedup"> (1239.3x slower)</span></td>
<td class="winner">Zig</td>
</tr>
</tbody>
</table>
</div>
<div class="category">
<h2>Class Queries</h2>
<div class="chart-container">
<canvas id="chart-2"></canvas>
</div>
<table class="stats-table">
<thead>
<tr>
<th>Benchmark</th>
<th>Zig</th><th>Chromium</th><th>Firefox</th><th>WebKit</th>
<th>Winner</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>Pure query: getElementsByClassName (100 elem)</strong></td>
<td>5µs<span class="speedup"> (73.5x slower)</span></td><td>101ns<span class="speedup"> (1.5x slower)</span></td><td>147ns<span class="speedup"> (2.2x slower)</span></td><td>68ns<span class="speedup"></span></td>
<td class="winner">WebKit</td>
</tr>
<tr>
<td><strong>Pure query: getElementsByClassName (1000 elem)</strong></td>
<td>6µs<span class="speedup"> (90.9x slower)</span></td><td>91ns<span class="speedup"> (1.4x slower)</span></td><td>156ns<span class="speedup"> (2.4x slower)</span></td><td>66ns<span class="speedup"></span></td>
<td class="winner">WebKit</td>
</tr>
<tr>
<td><strong>Pure query: getElementsByClassName (10000 elem)</strong></td>
<td>7µs<span class="speedup"> (104.5x slower)</span></td><td>94ns<span class="speedup"> (1.4x slower)</span></td><td>154ns<span class="speedup"> (2.3x slower)</span></td><td>67ns<span class="speedup"></span></td>
<td class="winner">WebKit</td>
</tr>
<tr>
<td><strong>Pure query: querySelector .class (100 elem)</strong></td>
<td>15ns<span class="speedup"></span></td><td>87ns<span class="speedup"> (5.8x slower)</span></td><td>180ns<span class="speedup"> (12.0x slower)</span></td><td>110ns<span class="speedup"> (7.3x slower)</span></td>
<td class="winner">Zig</td>
</tr>
<tr>
<td><strong>Pure query: querySelector .class (1000 elem)</strong></td>
<td>15ns<span class="speedup"></span></td><td>86ns<span class="speedup"> (5.7x slower)</span></td><td>180ns<span class="speedup"> (12.0x slower)</span></td><td>110ns<span class="speedup"> (7.3x slower)</span></td>
<td class="winner">Zig</td>
</tr>
<tr>
<td><strong>Pure query: querySelector .class (10000 elem)</strong></td>
<td>15ns<span class="speedup"></span></td><td>79ns<span class="speedup"> (5.3x slower)</span></td><td>180ns<span class="speedup"> (12.0x slower)</span></td><td>120ns<span class="speedup"> (8.0x slower)</span></td>
<td class="winner">Zig</td>
</tr>
</tbody>
</table>
</div>
<div class="category">
<h2>Complex Queries</h2>
<div class="chart-container">
<canvas id="chart-3"></canvas>
</div>
<table class="stats-table">
<thead>
<tr>
<th>Benchmark</th>
<th>Zig</th><th>Chromium</th><th>Firefox</th><th>WebKit</th>
<th>Winner</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>querySelector: Small DOM (100)</strong></td>
<td>37µs<span class="speedup"> (1.9x slower)</span></td><td>19µs<span class="speedup"></span></td><td>29µs<span class="speedup"> (1.5x slower)</span></td><td>24µs<span class="speedup"> (1.3x slower)</span></td>
<td class="winner">Chromium</td>
</tr>
<tr>
<td><strong>querySelector: Medium DOM (1000)</strong></td>
<td>226µs<span class="speedup"> (1.4x slower)</span></td><td>161µs<span class="speedup"></span></td><td>265µs<span class="speedup"> (1.7x slower)</span></td><td>236µs<span class="speedup"> (1.5x slower)</span></td>
<td class="winner">Chromium</td>
</tr>
<tr>
<td><strong>querySelector: Large DOM (10000)</strong></td>
<td>11ms<span class="speedup"> (7.4x slower)</span></td><td>1ms<span class="speedup"></span></td><td>3ms<span class="speedup"> (1.8x slower)</span></td><td>2ms<span class="speedup"> (1.4x slower)</span></td>
<td class="winner">Chromium</td>
</tr>
<tr>
<td><strong>querySelector: Class selector</strong></td>
<td>224µs<span class="speedup"> (1.4x slower)</span></td><td>159µs<span class="speedup"></span></td><td>255µs<span class="speedup"> (1.6x slower)</span></td><td>206µs<span class="speedup"> (1.3x slower)</span></td>
<td class="winner">Chromium</td>
</tr>
<tr>
<td><strong>SPA: Repeated queries (1000x)</strong></td>
<td>79µs<span class="speedup"> (1.5x slower)</span></td><td>54µs<span class="speedup"></span></td><td>98µs<span class="speedup"> (1.8x slower)</span></td><td>174µs<span class="speedup"> (3.2x slower)</span></td>
<td class="winner">Chromium</td>
</tr>
<tr>
<td><strong>SPA: Cold vs Hot cache (100x)</strong></td>
<td>452µs<span class="speedup"> (1.9x slower)</span></td><td>240µs<span class="speedup"></span></td><td>400µs<span class="speedup"> (1.7x slower)</span></td><td>371µs<span class="speedup"> (1.5x slower)</span></td>
<td class="winner">Chromium</td>
</tr>
</tbody>
</table>
</div>
<footer>
<p>Generated: 10/17/2025, 3:28:10 PM</p>
<p>Zig DOM Implementation - Benchmark Report</p>
</footer>
</div>
<script>
const implementations = ["Zig","Chromium","Firefox","WebKit"];
const colors = {
'Zig': '#f9a825',
'Chromium': '#4285f4',
'Firefox': '#ff6611',
'WebKit': '#00aaff'
};
const chartData = [{"category":"ID Queries","benchmarks":[{"name":"getElementById: Small DOM (100)","data":[29000,13944.22310756972,25819.265143992056,24875.62189054715]},{"name":"getElementById: Medium DOM (1000)","data":[216000,143099.99990463257,273631.8407960199,253240.27916251245]},{"name":"getElementById: Large DOM (10000)","data":[11000000,1496153.847529338,2816513.7614678903,2609999.9999999977]},{"name":"Pure query: getElementById (100 elem)","data":[5,93.29999995231628,101.99938800367198,76.999923000077]},{"name":"Pure query: getElementById (1000 elem)","data":[5,92.89962849685344,99.99920000639996,71.99942400460797]},{"name":"Pure query: getElementById (10000 elem)","data":[4,96.79912876015743,102.99979400041201,70.999787000639]},{"name":"Pure query: querySelector #id (100 elem)","data":[13,101.99388084398652,169.9932002719891,159.99200039998001]},{"name":"Pure query: querySelector #id (1000 elem)","data":[14,93.9943612919968,169.99660006799863,149.99850001500212]},{"name":"Pure query: querySelector #id (10000 elem)","data":[15,96.99224109754935,169.9949001529954,169.98470137687607]}]},{"category":"Tag Queries","benchmarks":[{"name":"Pure query: getElementsByTagName (100 elem)","data":[5000,89.49928400572796,120.99987900012101,73.99948200362597]},{"name":"Pure query: getElementsByTagName (1000 elem)","data":[5000,85.69940015188266,122.999754000492,72.999854000292]},{"name":"Pure query: getElementsByTagName (10000 elem)","data":[7000,83.89958059746444,136.999863000137,129.9988300105299]},{"name":"Pure query: querySelector tag (100 elem)","data":[15,205.99999904632568,289.9739023487886,239.97840194382505]},{"name":"Pure query: querySelector tag (1000 elem)","data":[15,1571.9842806340307,1069.9144068474523,1179.9528018879246]},{"name":"Pure query: querySelector tag (10000 elem)","data":[16,15792.000000476837,13050,19828.6119971602]}]},{"category":"Class Queries","benchmarks":[{"name":"Pure query: getElementsByClassName (100 elem)","data":[5000,101.49908650822142,146.999412002352,67.999932000068]},{"name":"Pure query: getElementsByClassName (1000 elem)","data":[6000,90.99990900009101,155.999532001404,65.99940600534595]},{"name":"Pure query: getElementsByClassName (10000 elem)","data":[7000,93.89953035929705,153.99907600554397,66.999933000067]},{"name":"Pure query: querySelector .class (100 elem)","data":[15,87.00000047683716,179.9982000179998,110]},{"name":"Pure query: querySelector .class (1000 elem)","data":[15,85.99225974294882,180,109.99890001099989]},{"name":"Pure query: querySelector .class (10000 elem)","data":[15,78.99999856948853,179.99280028798847,119.99160058795884]}]},{"category":"Complex Queries","benchmarks":[{"name":"querySelector: Small DOM (100)","data":[37000,19127.84954483138,28769.84126984127,23928.215353938183]},{"name":"querySelector: Medium DOM (1000)","data":[226000,160678.64271457086,265469.0618762475,236000]},{"name":"querySelector: Large DOM (10000)","data":[11000000,1492233.007856943,2627450.9803921566,2037735.8490566039]},{"name":"querySelector: Class selector","data":[224000,158771.0605031553,254980.0796812749,205765.40755467195]},{"name":"SPA: Repeated queries (1000x)","data":[79000,53646.353693989724,97902.0979020979,173956.26242544732]},{"name":"SPA: Cold vs Hot cache (100x)","data":[452000,239814.8156978466,400000,371428.57142857247]}]}];
// Create charts
chartData.forEach((category, index) => {
if (category.benchmarks.length === 0) return;
const ctx = document.getElementById(`chart-${index}`);
const datasets = implementations.map((impl, i) => ({
label: impl,
data: category.benchmarks.map(b => b.data[i]),
backgroundColor: colors[impl] || `hsl(${i * 60}, 70%, 60%)`,
borderColor: colors[impl] || `hsl(${i * 60}, 70%, 50%)`,
borderWidth: 2
}));
new Chart(ctx, {
type: 'bar',
data: {
labels: category.benchmarks.map(b => b.name.replace(/Pure query: /, '').replace(/ \(\d+ elem\)/, '')),
datasets: datasets
},
options: {
responsive: true,
maintainAspectRatio: false,
plugins: {
title: {
display: false
},
legend: {
position: 'top',
labels: {
font: {
size: 12,
weight: 600
},
padding: 15
}
},
tooltip: {
callbacks: {
label: function(context) {
let value = context.parsed.y;
if (value === null) return '';
let display;
if (value < 1000) {
display = Math.round(value) + 'ns';
} else if (value < 1000000) {
display = Math.round(value / 1000) + 'µs';
} else {
display = Math.round(value / 1000000) + 'ms';
}
return context.dataset.label + ': ' + display;
}
}
}
},
scales: {
y: {
type: 'logarithmic',
title: {
display: true,
text: 'Time per Operation (ns, log scale)',
font: {
size: 14,
weight: 600
}
},
ticks: {
callback: function(value) {
if (value < 1000) return value + 'ns';
if (value < 1000000) return (value / 1000) + 'µs';
return (value / 1000000) + 'ms';
}
}
},
x: {
ticks: {
font: {
size: 11
}
}
}
}
}
});
});
</script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment