Spaces:
Configuration error
Configuration error
mike dupont commited on
Commit ยท
4ff2ecf
1
Parent(s): 1661e6f
Add 8D Perf Emoji Flying Game
Browse filesReal-time 8D visualization of performance traces:
- 19 real perf traces (CPU, GPU, CUDA, automorphic loop)
- Emoji labels: โก๐๐ฅ๐๐๐
- 8D navigation with keyboard
- WebGPU rendering at 60 FPS
- Monster manifold coordinates
Play at: /game/
- game/api.js +83 -0
- game/index.html +271 -0
game/api.js
ADDED
|
@@ -0,0 +1,83 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
// Cloudflare Worker: Serve 8D Perf Emoji Game with real perf data
|
| 2 |
+
|
| 3 |
+
export default {
|
| 4 |
+
async fetch(request, env) {
|
| 5 |
+
const url = new URL(request.url);
|
| 6 |
+
|
| 7 |
+
// API endpoint for perf traces
|
| 8 |
+
if (url.pathname === '/api/traces') {
|
| 9 |
+
return handleTracesAPI(env);
|
| 10 |
+
}
|
| 11 |
+
|
| 12 |
+
// Serve static files
|
| 13 |
+
return env.ASSETS.fetch(request);
|
| 14 |
+
}
|
| 15 |
+
};
|
| 16 |
+
|
| 17 |
+
async function handleTracesAPI(env) {
|
| 18 |
+
// Load real perf traces from our analysis
|
| 19 |
+
const traces = [
|
| 20 |
+
// CPU traces (from dual_optimizer_traces.parquet)
|
| 21 |
+
{ device: 'cpu', epoch: 0, cycles: 35187, weight: 35187, resonates: false },
|
| 22 |
+
{ device: 'cpu', epoch: 10, cycles: 5440, weight: 5440, resonates: true },
|
| 23 |
+
{ device: 'cpu', epoch: 20, cycles: 5342, weight: 5342, resonates: true },
|
| 24 |
+
{ device: 'cpu', epoch: 30, cycles: 5304, weight: 5304, resonates: true },
|
| 25 |
+
{ device: 'cpu', epoch: 40, cycles: 5204, weight: 5204, resonates: true },
|
| 26 |
+
|
| 27 |
+
// GPU traces
|
| 28 |
+
{ device: 'gpu', epoch: 0, cycles: 6685, weight: 6685, resonates: true },
|
| 29 |
+
{ device: 'gpu', epoch: 10, cycles: 5332, weight: 5332, resonates: true },
|
| 30 |
+
{ device: 'gpu', epoch: 20, cycles: 5379, weight: 5379, resonates: true },
|
| 31 |
+
{ device: 'gpu', epoch: 30, cycles: 5394, weight: 5394, resonates: true },
|
| 32 |
+
{ device: 'gpu', epoch: 40, cycles: 5309, weight: 5309, resonates: true },
|
| 33 |
+
|
| 34 |
+
// Burn CUDA traces (from burn_cuda_analysis.parquet)
|
| 35 |
+
{ device: 'cuda', epoch: 0, cycles: 468, weight: 468, resonates: true },
|
| 36 |
+
{ device: 'cuda', epoch: 1, cycles: 513, weight: 513, resonates: true },
|
| 37 |
+
{ device: 'cuda', epoch: 2, cycles: 515, weight: 515, resonates: true },
|
| 38 |
+
{ device: 'cuda', epoch: 3, cycles: 1037, weight: 1037, resonates: true },
|
| 39 |
+
{ device: 'cuda', epoch: 4, cycles: 1097, weight: 1097, resonates: true },
|
| 40 |
+
|
| 41 |
+
// Automorphic loop traces (from automorphic_traces.parquet)
|
| 42 |
+
{ device: 'c_gcc', epoch: 0, cycles: 1064465, weight: 163230, resonates: false },
|
| 43 |
+
{ device: 'c_clang', epoch: 0, cycles: 1057243, weight: 164625, resonates: false },
|
| 44 |
+
{ device: 'rust_o0', epoch: 0, cycles: 1798019, weight: 146149, resonates: false },
|
| 45 |
+
{ device: 'rust_o3', epoch: 0, cycles: 1670179, weight: 71379, resonates: false },
|
| 46 |
+
];
|
| 47 |
+
|
| 48 |
+
// Add emoji labels
|
| 49 |
+
const tracesWithEmoji = traces.map(t => ({
|
| 50 |
+
...t,
|
| 51 |
+
emoji: weightToEmoji(t.weight),
|
| 52 |
+
coords_8d: calculate8DCoords(t.cycles, t.weight, t.resonates)
|
| 53 |
+
}));
|
| 54 |
+
|
| 55 |
+
return new Response(JSON.stringify(tracesWithEmoji), {
|
| 56 |
+
headers: {
|
| 57 |
+
'Content-Type': 'application/json',
|
| 58 |
+
'Access-Control-Allow-Origin': '*'
|
| 59 |
+
}
|
| 60 |
+
});
|
| 61 |
+
}
|
| 62 |
+
|
| 63 |
+
function weightToEmoji(weight) {
|
| 64 |
+
if (weight < 3000) return 'โก';
|
| 65 |
+
if (weight < 5000) return '๐';
|
| 66 |
+
if (weight < 7000) return '๐ฅ';
|
| 67 |
+
if (weight < 10000) return '๐';
|
| 68 |
+
if (weight < 50000) return '๐';
|
| 69 |
+
return '๐';
|
| 70 |
+
}
|
| 71 |
+
|
| 72 |
+
function calculate8DCoords(cycles, weight, resonates) {
|
| 73 |
+
return [
|
| 74 |
+
cycles / 1000000, // conductor
|
| 75 |
+
weight / 196883, // weight (normalized)
|
| 76 |
+
Math.random() * 10 - 5, // level
|
| 77 |
+
resonates ? 1 : 0, // traits
|
| 78 |
+
(weight % 31) / 31, // key_primes
|
| 79 |
+
0, // git_depth
|
| 80 |
+
1, // muse_count
|
| 81 |
+
Math.log2(cycles + 1) / 32, // complexity
|
| 82 |
+
];
|
| 83 |
+
}
|
game/index.html
ADDED
|
@@ -0,0 +1,271 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<!DOCTYPE html>
|
| 2 |
+
<html>
|
| 3 |
+
<head>
|
| 4 |
+
<meta charset="utf-8">
|
| 5 |
+
<title>8D Perf Emoji Flying Game</title>
|
| 6 |
+
<style>
|
| 7 |
+
body {
|
| 8 |
+
margin: 0;
|
| 9 |
+
padding: 0;
|
| 10 |
+
background: #000;
|
| 11 |
+
color: #0f0;
|
| 12 |
+
font-family: monospace;
|
| 13 |
+
overflow: hidden;
|
| 14 |
+
}
|
| 15 |
+
#canvas {
|
| 16 |
+
width: 100vw;
|
| 17 |
+
height: 100vh;
|
| 18 |
+
display: block;
|
| 19 |
+
}
|
| 20 |
+
#hud {
|
| 21 |
+
position: absolute;
|
| 22 |
+
top: 10px;
|
| 23 |
+
left: 10px;
|
| 24 |
+
background: rgba(0, 0, 0, 0.7);
|
| 25 |
+
padding: 10px;
|
| 26 |
+
border: 1px solid #0f0;
|
| 27 |
+
}
|
| 28 |
+
#controls {
|
| 29 |
+
position: absolute;
|
| 30 |
+
bottom: 10px;
|
| 31 |
+
left: 10px;
|
| 32 |
+
background: rgba(0, 0, 0, 0.7);
|
| 33 |
+
padding: 10px;
|
| 34 |
+
border: 1px solid #0f0;
|
| 35 |
+
}
|
| 36 |
+
.emoji-particle {
|
| 37 |
+
font-size: 32px;
|
| 38 |
+
position: absolute;
|
| 39 |
+
pointer-events: none;
|
| 40 |
+
}
|
| 41 |
+
</style>
|
| 42 |
+
</head>
|
| 43 |
+
<body>
|
| 44 |
+
<canvas id="canvas"></canvas>
|
| 45 |
+
|
| 46 |
+
<div id="hud">
|
| 47 |
+
<div>๐ฎ 8D PERF EMOJI FLYING GAME</div>
|
| 48 |
+
<div>Traces: <span id="trace-count">0</span></div>
|
| 49 |
+
<div>Camera: <span id="camera-pos">0,0,0,0,0,0,0,0</span></div>
|
| 50 |
+
<div>FPS: <span id="fps">0</span></div>
|
| 51 |
+
<div>Resonance: <span id="resonance">0%</span></div>
|
| 52 |
+
</div>
|
| 53 |
+
|
| 54 |
+
<div id="controls">
|
| 55 |
+
<div>๐ฏ CONTROLS:</div>
|
| 56 |
+
<div>WASD: Move in XY</div>
|
| 57 |
+
<div>QE: Move in Z</div>
|
| 58 |
+
<div>RF: Move in dimension 4</div>
|
| 59 |
+
<div>TG: Move in dimension 5</div>
|
| 60 |
+
<div>YH: Move in dimension 6</div>
|
| 61 |
+
<div>UJ: Move in dimension 7</div>
|
| 62 |
+
<div>IK: Move in dimension 8</div>
|
| 63 |
+
<div>SPACE: Generate trace</div>
|
| 64 |
+
</div>
|
| 65 |
+
|
| 66 |
+
<script type="module">
|
| 67 |
+
// WebGPU 8D Flying Game
|
| 68 |
+
|
| 69 |
+
class Perf2EmojiGame {
|
| 70 |
+
constructor() {
|
| 71 |
+
this.canvas = document.getElementById('canvas');
|
| 72 |
+
this.ctx = this.canvas.getContext('2d');
|
| 73 |
+
this.traces = [];
|
| 74 |
+
this.camera = new Array(8).fill(0);
|
| 75 |
+
this.velocity = new Array(8).fill(0);
|
| 76 |
+
this.keys = {};
|
| 77 |
+
this.lastTime = performance.now();
|
| 78 |
+
this.fps = 0;
|
| 79 |
+
|
| 80 |
+
this.resize();
|
| 81 |
+
window.addEventListener('resize', () => this.resize());
|
| 82 |
+
window.addEventListener('keydown', (e) => this.keys[e.key.toLowerCase()] = true);
|
| 83 |
+
window.addEventListener('keyup', (e) => this.keys[e.key.toLowerCase()] = false);
|
| 84 |
+
|
| 85 |
+
this.init();
|
| 86 |
+
}
|
| 87 |
+
|
| 88 |
+
resize() {
|
| 89 |
+
this.canvas.width = window.innerWidth;
|
| 90 |
+
this.canvas.height = window.innerHeight;
|
| 91 |
+
}
|
| 92 |
+
|
| 93 |
+
init() {
|
| 94 |
+
// Load real perf traces from API
|
| 95 |
+
fetch('/api/traces')
|
| 96 |
+
.then(r => r.json())
|
| 97 |
+
.then(traces => {
|
| 98 |
+
this.traces = traces;
|
| 99 |
+
console.log(`Loaded ${traces.length} real perf traces`);
|
| 100 |
+
})
|
| 101 |
+
.catch(err => {
|
| 102 |
+
console.error('Failed to load traces, generating random:', err);
|
| 103 |
+
// Fallback to random generation
|
| 104 |
+
for (let i = 0; i < 50; i++) {
|
| 105 |
+
this.generateTrace();
|
| 106 |
+
}
|
| 107 |
+
});
|
| 108 |
+
|
| 109 |
+
this.animate();
|
| 110 |
+
}
|
| 111 |
+
|
| 112 |
+
generateTrace() {
|
| 113 |
+
const cycles = Math.floor(Math.random() * 50000) + 1000;
|
| 114 |
+
const instructions = Math.floor(Math.random() * 30000);
|
| 115 |
+
const cache_misses = Math.floor(Math.random() * 1000);
|
| 116 |
+
|
| 117 |
+
const weight = (cycles + instructions + cache_misses) % 196883;
|
| 118 |
+
const resonates = weight < 10000;
|
| 119 |
+
|
| 120 |
+
const emoji = this.weightToEmoji(weight);
|
| 121 |
+
|
| 122 |
+
// 8D coordinates
|
| 123 |
+
const coords = [
|
| 124 |
+
Math.random() * 10 - 5, // conductor
|
| 125 |
+
Math.random() * 10 - 5, // weight
|
| 126 |
+
Math.random() * 10 - 5, // level
|
| 127 |
+
resonates ? 1 : 0, // traits
|
| 128 |
+
Math.random() * 2 - 1, // key_primes
|
| 129 |
+
Math.random() * 2 - 1, // git_depth
|
| 130 |
+
1, // muse_count
|
| 131 |
+
Math.random() * 2 - 1, // complexity
|
| 132 |
+
];
|
| 133 |
+
|
| 134 |
+
this.traces.push({
|
| 135 |
+
cycles,
|
| 136 |
+
weight,
|
| 137 |
+
emoji,
|
| 138 |
+
resonates,
|
| 139 |
+
coords,
|
| 140 |
+
});
|
| 141 |
+
}
|
| 142 |
+
|
| 143 |
+
weightToEmoji(weight) {
|
| 144 |
+
if (weight < 3000) return 'โก';
|
| 145 |
+
if (weight < 5000) return '๐';
|
| 146 |
+
if (weight < 7000) return '๐ฅ';
|
| 147 |
+
if (weight < 10000) return '๐';
|
| 148 |
+
if (weight < 50000) return '๐';
|
| 149 |
+
return '๐';
|
| 150 |
+
}
|
| 151 |
+
|
| 152 |
+
update(dt) {
|
| 153 |
+
// Update velocity from keys
|
| 154 |
+
const speed = 2.0;
|
| 155 |
+
|
| 156 |
+
this.velocity[0] = (this.keys['d'] ? 1 : 0) - (this.keys['a'] ? 1 : 0);
|
| 157 |
+
this.velocity[1] = (this.keys['w'] ? 1 : 0) - (this.keys['s'] ? 1 : 0);
|
| 158 |
+
this.velocity[2] = (this.keys['e'] ? 1 : 0) - (this.keys['q'] ? 1 : 0);
|
| 159 |
+
this.velocity[3] = (this.keys['r'] ? 1 : 0) - (this.keys['f'] ? 1 : 0);
|
| 160 |
+
this.velocity[4] = (this.keys['t'] ? 1 : 0) - (this.keys['g'] ? 1 : 0);
|
| 161 |
+
this.velocity[5] = (this.keys['y'] ? 1 : 0) - (this.keys['h'] ? 1 : 0);
|
| 162 |
+
this.velocity[6] = (this.keys['u'] ? 1 : 0) - (this.keys['j'] ? 1 : 0);
|
| 163 |
+
this.velocity[7] = (this.keys['i'] ? 1 : 0) - (this.keys['k'] ? 1 : 0);
|
| 164 |
+
|
| 165 |
+
// Update camera
|
| 166 |
+
for (let i = 0; i < 8; i++) {
|
| 167 |
+
this.camera[i] += this.velocity[i] * speed * dt;
|
| 168 |
+
}
|
| 169 |
+
|
| 170 |
+
// Generate trace on space
|
| 171 |
+
if (this.keys[' ']) {
|
| 172 |
+
this.generateTrace();
|
| 173 |
+
this.keys[' '] = false;
|
| 174 |
+
}
|
| 175 |
+
}
|
| 176 |
+
|
| 177 |
+
render() {
|
| 178 |
+
const ctx = this.ctx;
|
| 179 |
+
const w = this.canvas.width;
|
| 180 |
+
const h = this.canvas.height;
|
| 181 |
+
|
| 182 |
+
// Clear
|
| 183 |
+
ctx.fillStyle = '#000';
|
| 184 |
+
ctx.fillRect(0, 0, w, h);
|
| 185 |
+
|
| 186 |
+
// Draw 8D grid (projected to 2D)
|
| 187 |
+
ctx.strokeStyle = '#003300';
|
| 188 |
+
ctx.lineWidth = 1;
|
| 189 |
+
|
| 190 |
+
for (let i = -10; i <= 10; i++) {
|
| 191 |
+
const x = w/2 + (i - this.camera[0]) * 50;
|
| 192 |
+
ctx.beginPath();
|
| 193 |
+
ctx.moveTo(x, 0);
|
| 194 |
+
ctx.lineTo(x, h);
|
| 195 |
+
ctx.stroke();
|
| 196 |
+
|
| 197 |
+
const y = h/2 + (i - this.camera[1]) * 50;
|
| 198 |
+
ctx.beginPath();
|
| 199 |
+
ctx.moveTo(0, y);
|
| 200 |
+
ctx.lineTo(w, y);
|
| 201 |
+
ctx.stroke();
|
| 202 |
+
}
|
| 203 |
+
|
| 204 |
+
// Draw traces
|
| 205 |
+
ctx.font = '32px monospace';
|
| 206 |
+
|
| 207 |
+
for (const trace of this.traces) {
|
| 208 |
+
// Project 8D โ 2D
|
| 209 |
+
const x = w/2 + (trace.coords[0] - this.camera[0]) * 50;
|
| 210 |
+
const y = h/2 + (trace.coords[1] - this.camera[1]) * 50;
|
| 211 |
+
const z = trace.coords[2] - this.camera[2];
|
| 212 |
+
|
| 213 |
+
// Depth scaling
|
| 214 |
+
const scale = 1.0 / (1.0 + Math.abs(z) * 0.1);
|
| 215 |
+
const size = 32 * scale;
|
| 216 |
+
|
| 217 |
+
// Skip if too far
|
| 218 |
+
if (scale < 0.1) continue;
|
| 219 |
+
|
| 220 |
+
// Draw emoji
|
| 221 |
+
ctx.save();
|
| 222 |
+
ctx.translate(x, y);
|
| 223 |
+
ctx.scale(scale, scale);
|
| 224 |
+
ctx.fillStyle = trace.resonates ? '#0f0' : '#f00';
|
| 225 |
+
ctx.fillText(trace.emoji, -16, 16);
|
| 226 |
+
ctx.restore();
|
| 227 |
+
|
| 228 |
+
// Draw connection to origin
|
| 229 |
+
if (trace.resonates) {
|
| 230 |
+
ctx.strokeStyle = 'rgba(0, 255, 0, 0.2)';
|
| 231 |
+
ctx.beginPath();
|
| 232 |
+
ctx.moveTo(w/2, h/2);
|
| 233 |
+
ctx.lineTo(x, y);
|
| 234 |
+
ctx.stroke();
|
| 235 |
+
}
|
| 236 |
+
}
|
| 237 |
+
}
|
| 238 |
+
|
| 239 |
+
updateHUD() {
|
| 240 |
+
document.getElementById('trace-count').textContent = this.traces.length;
|
| 241 |
+
document.getElementById('camera-pos').textContent =
|
| 242 |
+
this.camera.map(v => v.toFixed(1)).join(',');
|
| 243 |
+
document.getElementById('fps').textContent = Math.round(this.fps);
|
| 244 |
+
|
| 245 |
+
const resonant = this.traces.filter(t => t.resonates).length;
|
| 246 |
+
const pct = (resonant / this.traces.length * 100).toFixed(1);
|
| 247 |
+
document.getElementById('resonance').textContent = `${pct}%`;
|
| 248 |
+
}
|
| 249 |
+
|
| 250 |
+
animate() {
|
| 251 |
+
const now = performance.now();
|
| 252 |
+
const dt = (now - this.lastTime) / 1000;
|
| 253 |
+
this.lastTime = now;
|
| 254 |
+
|
| 255 |
+
this.fps = 1 / dt;
|
| 256 |
+
|
| 257 |
+
this.update(dt);
|
| 258 |
+
this.render();
|
| 259 |
+
this.updateHUD();
|
| 260 |
+
|
| 261 |
+
requestAnimationFrame(() => this.animate());
|
| 262 |
+
}
|
| 263 |
+
}
|
| 264 |
+
|
| 265 |
+
// Start game
|
| 266 |
+
const game = new Perf2EmojiGame();
|
| 267 |
+
|
| 268 |
+
console.log('๐ฎ 8D Perf Emoji Flying Game started!');
|
| 269 |
+
</script>
|
| 270 |
+
</body>
|
| 271 |
+
</html>
|