Skip to content

Commit 2ef071b

Browse files
authored
Add WASM bouncing ball demo (#21)
1 parent fa2f036 commit 2ef071b

File tree

3 files changed

+192
-6
lines changed

3 files changed

+192
-6
lines changed

wasm/game.js

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
const canvas = document.getElementById('game-canvas');
2+
const context = canvas.getContext('2d');
3+
let wasmExports = null;
4+
let lastTime = null;
5+
6+
function resizeCanvas() {
7+
canvas.width = window.innerWidth;
8+
canvas.height = window.innerHeight;
9+
10+
if (wasmExports) {
11+
wasmExports.set_canvas_size(canvas.width, canvas.height);
12+
}
13+
}
14+
15+
function drawFrame() {
16+
if (!wasmExports) {
17+
return;
18+
}
19+
20+
context.clearRect(0, 0, canvas.width, canvas.height);
21+
context.fillStyle = '#0d47a1';
22+
context.fillRect(0, 0, canvas.width, canvas.height);
23+
24+
const radius = wasmExports.get_ball_radius();
25+
const x = wasmExports.get_ball_x();
26+
const y = wasmExports.get_ball_y();
27+
28+
context.fillStyle = '#ffca28';
29+
context.beginPath();
30+
context.arc(x, y, radius, 0, Math.PI * 2);
31+
context.fill();
32+
}
33+
34+
function updateAndRender(timestamp) {
35+
if (!lastTime) {
36+
lastTime = timestamp;
37+
}
38+
39+
const deltaSeconds = (timestamp - lastTime) / 1000;
40+
lastTime = timestamp;
41+
42+
wasmExports.update(deltaSeconds);
43+
drawFrame();
44+
45+
requestAnimationFrame(updateAndRender);
46+
}
47+
48+
async function start() {
49+
const response = await fetch('hello.wasm');
50+
const bytes = await response.arrayBuffer();
51+
const { instance } = await WebAssembly.instantiate(bytes, {});
52+
wasmExports = instance.exports;
53+
54+
resizeCanvas();
55+
wasmExports.reset_ball();
56+
drawFrame();
57+
requestAnimationFrame(updateAndRender);
58+
}
59+
60+
window.addEventListener('resize', resizeCanvas);
61+
start();

wasm/hello.c

Lines changed: 83 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,93 @@
1-
#include <stddef.h>
21
#include <emscripten/emscripten.h>
32

4-
static const char GREETING[] = "Hello from WebAssembly!";
3+
static float canvas_width = 640.0f;
4+
static float canvas_height = 480.0f;
5+
static float ball_x = 320.0f;
6+
static float ball_y = 240.0f;
7+
static float ball_radius = 16.0f;
8+
static float velocity_x = 180.0f;
9+
static float velocity_y = 140.0f;
10+
11+
static void clamp_ball_inside_canvas(void)
12+
{
13+
if (ball_x < ball_radius) {
14+
ball_x = ball_radius;
15+
} else if (ball_x > canvas_width - ball_radius) {
16+
ball_x = canvas_width - ball_radius;
17+
}
18+
19+
if (ball_y < ball_radius) {
20+
ball_y = ball_radius;
21+
} else if (ball_y > canvas_height - ball_radius) {
22+
ball_y = canvas_height - ball_radius;
23+
}
24+
}
25+
26+
EMSCRIPTEN_KEEPALIVE
27+
void set_canvas_size(int width, int height)
28+
{
29+
if (width > 0) {
30+
canvas_width = (float)width;
31+
}
32+
33+
if (height > 0) {
34+
canvas_height = (float)height;
35+
}
36+
37+
clamp_ball_inside_canvas();
38+
}
39+
40+
EMSCRIPTEN_KEEPALIVE
41+
void reset_ball(void)
42+
{
43+
ball_x = canvas_width * 0.5f;
44+
ball_y = canvas_height * 0.5f;
45+
velocity_x = 180.0f;
46+
velocity_y = 140.0f;
47+
clamp_ball_inside_canvas();
48+
}
49+
50+
EMSCRIPTEN_KEEPALIVE
51+
void update(float delta_seconds)
52+
{
53+
if (delta_seconds <= 0.0f) {
54+
return;
55+
}
56+
57+
ball_x += velocity_x * delta_seconds;
58+
ball_y += velocity_y * delta_seconds;
59+
60+
if (ball_x - ball_radius < 0.0f) {
61+
ball_x = ball_radius;
62+
velocity_x = -velocity_x;
63+
} else if (ball_x + ball_radius > canvas_width) {
64+
ball_x = canvas_width - ball_radius;
65+
velocity_x = -velocity_x;
66+
}
67+
68+
if (ball_y - ball_radius < 0.0f) {
69+
ball_y = ball_radius;
70+
velocity_y = -velocity_y;
71+
} else if (ball_y + ball_radius > canvas_height) {
72+
ball_y = canvas_height - ball_radius;
73+
velocity_y = -velocity_y;
74+
}
75+
}
76+
77+
EMSCRIPTEN_KEEPALIVE
78+
float get_ball_x(void)
79+
{
80+
return ball_x;
81+
}
582

683
EMSCRIPTEN_KEEPALIVE
7-
const char *get_greeting(void)
84+
float get_ball_y(void)
885
{
9-
return GREETING;
86+
return ball_y;
1087
}
1188

1289
EMSCRIPTEN_KEEPALIVE
13-
size_t get_greeting_length(void)
90+
float get_ball_radius(void)
1491
{
15-
return sizeof(GREETING) - 1;
92+
return ball_radius;
1693
}

wasm/index.html

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="utf-8" />
5+
<meta name="viewport" content="width=device-width, initial-scale=1" />
6+
<title>Minimal WASM Bouncing Ball</title>
7+
<style>
8+
html,
9+
body {
10+
margin: 0;
11+
height: 100%;
12+
background: #111827;
13+
color: #f9fafb;
14+
font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
15+
display: flex;
16+
align-items: center;
17+
justify-content: center;
18+
flex-direction: column;
19+
}
20+
21+
h1 {
22+
font-size: 1.25rem;
23+
font-weight: 600;
24+
margin: 1rem 0 0.5rem;
25+
}
26+
27+
p {
28+
margin: 0 1.5rem 1.5rem;
29+
max-width: 32rem;
30+
text-align: center;
31+
line-height: 1.5;
32+
}
33+
34+
canvas {
35+
flex: 1;
36+
width: 100%;
37+
height: 100%;
38+
display: block;
39+
}
40+
</style>
41+
</head>
42+
<body>
43+
<h1>WebAssembly Bouncing Ball Demo</h1>
44+
<p>The ball is simulated inside WebAssembly and rendered on an HTML canvas.</p>
45+
<canvas id="game-canvas"></canvas>
46+
<script type="module" src="game.js"></script>
47+
</body>
48+
</html>

0 commit comments

Comments
 (0)