Skip to content

Commit f3ff16e

Browse files
committed
Initial commit
0 parents  commit f3ff16e

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

55 files changed

+1933
-0
lines changed

.gitignore

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
node_modules/
2+
package-lock.json

README.md

+55
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
![](https://avatars1.githubusercontent.com/u/28916798?s=64) AssemblyScript Examples
2+
=======================
3+
4+
## Instructions
5+
6+
This repository contains one example per directory. All examples can be obtained by cloning the repository:
7+
8+
```
9+
$> git clone https://github.com/AssemblyScript/examples.git
10+
$> cd examples
11+
```
12+
13+
Afterwards, select the example you'd like to run and follow the instructions in its README file.
14+
15+
## Low-level perspective
16+
17+
The following examples make use of AssemblyScript's low-level capabilities, i.e. to essentially write WebAssembly with a nicer syntax.
18+
19+
* [Conway's Game Of Life](./game-of-life) ([demo](https://assemblyscript.github.io/examples/game-of-life/))<br />
20+
An implementation of the game of life with slight modifications. Updates an image buffer in memory, that is then presented on a canvas.
21+
22+
<img src="./game-of-life/preview.jpg" />
23+
24+
* [Mandelbrot Set](./mandelbrot) ([demo](https://assemblyscript.github.io/examples/mandelbrot/))<br />
25+
Computes 2048 offsets of a color gradient in memory, line by line, and presents the set using the gradient's actual colors, as computed on the JavaScript side, on a canvas.
26+
27+
<img src="./mandelbrot/preview.jpg" />
28+
29+
* [Interference effect](./interference) ([demo](https://assemblyscript.github.io/examples/interference/))<br />
30+
Colin Eberhardt's and Ben Smith's WebAssembly interference effect, if it was written in AssemblyScript.
31+
32+
<img src="./interference/preview.jpg" />
33+
34+
## High-level perspective
35+
36+
These examples cover slightly higher level aspects, like working with managed objects or interfacing with them.
37+
38+
* [N-body system](./n-body) ([demo](https://assemblyscript.github.io/examples/n-body/))<br />
39+
This is actually a benchmark - visualizing it just so happened.
40+
41+
<img src="./n-body/preview.jpg" />
42+
43+
* [Loader](./loader)<br />
44+
Utilizes the [loader](https://docs.assemblyscript.org/basics/loader) to perform various common tasks on the WebAssembly/JavaScript boundary, like passing along strings and arrays between both worlds.
45+
46+
<img src="./loader/preview.jpg" />
47+
48+
## Other
49+
50+
General examples showing how to utilize specific AssemblyScript features.
51+
52+
* [Browser SDK](./sdk)<br />
53+
Shows how to use the browser SDK to run the AssemblyScript compiler in the browser.
54+
55+
<img src="./sdk/preview.jpg" />

game-of-life/README.md

+27
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
Conway's Game of Life
2+
=====================
3+
4+
An [AssemblyScript](http://assemblyscript.org) example. Continuously updates the cellular automaton and visualizes its state on a canvas. Compiles to ~940 bytes of optimized WASM.
5+
6+
Instructions
7+
------------
8+
9+
First, install the development dependencies:
10+
11+
```
12+
$> npm install
13+
```
14+
15+
Now, to build [assembly/index.ts](./assembly/index.ts) to an untouched and an optimized `.wasm` including their respective `.wat` representations, run:
16+
17+
```
18+
$> npm run asbuild
19+
```
20+
21+
Afterwards, run
22+
23+
```
24+
$> npm start
25+
```
26+
27+
to start a <a href="http://localhost:9080">local server</a>. Should also automatically launch a browser.

game-of-life/assembly/config.ts

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
// On the WASM side, 32-bit color values are modified in ABGR order (alpha, blue, green, red)
2+
// because WASM is little endian. This results in RGBA in memory, which is exactly what the image
3+
// buffer, composed of 8-bit components, expects on the JS side.
4+
export declare const BGR_ALIVE: u32;
5+
export declare const BGR_DEAD: u32;
6+
export declare const BIT_ROT: u32;

game-of-life/assembly/index.ts

+93
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
// see: https://en.wikipedia.org/wiki/Conway%27s_Game_of_Life
2+
3+
// Configuration imported from JS
4+
import { BGR_ALIVE, BGR_DEAD, BIT_ROT } from "./config";
5+
6+
var width: i32, height: i32, offset: i32;
7+
8+
/** Gets an input pixel in the range [0, s]. */
9+
@inline
10+
function get(x: u32, y: u32): u32 {
11+
return load<u32>((y * width + x) << 2);
12+
}
13+
14+
/** Sets an output pixel in the range [s, 2*s]. */
15+
@inline
16+
function set(x: u32, y: u32, v: u32): void {
17+
store<u32>((offset + y * width + x) << 2, v);
18+
}
19+
20+
/** Sets an output pixel in the range [s, 2*s] while fading it out. */
21+
@inline
22+
function rot(x: u32, y: u32, v: u32): void {
23+
var alpha = max<i32>((v >> 24) - BIT_ROT, 0);
24+
set(x, y, (alpha << 24) | (v & 0x00ffffff));
25+
}
26+
27+
/** Initializes width and height. Called once from JS. */
28+
export function init(w: i32, h: i32): void {
29+
width = w;
30+
height = h;
31+
offset = w * h;
32+
33+
// Start by filling output with random live cells.
34+
for (let y = 0; y < h; ++y) {
35+
for (let x = 0; x < w; ++x) {
36+
let c = Math.random() > 0.1
37+
? BGR_DEAD & 0x00ffffff
38+
: BGR_ALIVE | 0xff000000;
39+
set(x, y, c);
40+
}
41+
}
42+
}
43+
44+
/** Performs one step. Called about 30 times a second from JS. */
45+
export function step(): void {
46+
var w = width,
47+
h = height;
48+
49+
var hm1 = h - 1, // h - 1
50+
wm1 = w - 1; // w - 1
51+
52+
// The universe of the Game of Life is an infinite two-dimensional orthogonal grid of square
53+
// "cells", each of which is in one of two possible states, alive or dead.
54+
for (let y = 0; y < h; ++y) {
55+
let ym1 = y == 0 ? hm1 : y - 1,
56+
yp1 = y == hm1 ? 0 : y + 1;
57+
for (let x = 0; x < w; ++x) {
58+
let xm1 = x == 0 ? wm1 : x - 1,
59+
xp1 = x == wm1 ? 0 : x + 1;
60+
61+
// Every cell interacts with its eight neighbours, which are the cells that are horizontally,
62+
// vertically, or diagonally adjacent. Least significant bit indicates alive or dead.
63+
let aliveNeighbors = (
64+
(get(xm1, ym1) & 1) + (get(x, ym1) & 1) + (get(xp1, ym1) & 1) +
65+
(get(xm1, y ) & 1) + (get(xp1, y ) & 1) +
66+
(get(xm1, yp1) & 1) + (get(x, yp1) & 1) + (get(xp1, yp1) & 1)
67+
);
68+
69+
let self = get(x, y);
70+
if (self & 1) {
71+
// A live cell with 2 or 3 live neighbors rots on to the next generation.
72+
if ((aliveNeighbors & 0b1110) == 0b0010) rot(x, y, self);
73+
// A live cell with fewer than 2 or more than 3 live neighbors dies.
74+
else set(x, y, BGR_DEAD | 0xff000000);
75+
} else {
76+
// A dead cell with exactly 3 live neighbors becomes a live cell.
77+
if (aliveNeighbors == 3) set(x, y, BGR_ALIVE | 0xff000000);
78+
// A dead cell with fewer or more than 3 live neighbors just rots.
79+
else rot(x, y, self);
80+
}
81+
}
82+
}
83+
}
84+
85+
/** Fills the row and column indicated by `x` and `y` with random live cells. */
86+
export function fill(x: u32, y: u32, p: f64): void {
87+
for (let ix = 0; ix < width; ++ix) {
88+
if (Math.random() < p) set(ix, y, BGR_ALIVE | 0xff000000);
89+
}
90+
for (let iy = 0; iy < height; ++iy) {
91+
if (Math.random() < p) set(x, iy, BGR_ALIVE | 0xff000000);
92+
}
93+
}

game-of-life/assembly/tsconfig.json

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"extends": "../node_modules/assemblyscript/std/assembly.json",
3+
"include": [
4+
"./**/*.ts"
5+
]
6+
}

game-of-life/build/.gitignore

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
*
2+
!.gitignore

game-of-life/index.html

+136
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
<!DOCTYPE html>
2+
<html>
3+
<head>
4+
<title>Conway's Game of Life - AssemblyScript</title>
5+
<link rel="icon" href="https://assemblyscript.org/favicon.ico" type="image/x-icon" />
6+
<meta name="viewport" content="user-scalable=0" />
7+
<style>
8+
html, body { height: 100%; margin: 0; overflow: hidden; color: #111; background: #fff; font-family: sans-serif; }
9+
body { border-top: 2px solid #bc18d4; }
10+
h1 { padding: 18px 20px 20px; font-size: 12pt; margin: 0; }
11+
a { color: #111; text-decoration: none; }
12+
a:hover { color: #bc18d4; text-decoration: underline; }
13+
canvas { position: absolute; top: 60px; left: 20px; width: calc(100% - 40px); height: calc(100% - 80px); background: #100707; cursor: cell; user-select: none; }
14+
#edge { position: absolute; bottom: 40px; right: 40px; color: #fff; display: none; text-shadow: 0 1px 2px #000; -ms-user-select: none; user-select: none; }
15+
</style>
16+
</head>
17+
<body>
18+
<h1>
19+
<a href="https://en.wikipedia.org/wiki/Conway's_Game_of_Life">Conway's Game of Life</a> in
20+
<a href="http://assemblyscript.org">AssemblyScript</a>
21+
( <a href="https://github.com/AssemblyScript/examples/blob/master/game-of-life/assembly/index.ts">source</a> )
22+
</h1>
23+
<canvas></canvas>
24+
<div id="edge">Might be blurry because MS Edge does not support 'image-rendering: crisp-edges' (yet) :-(</div>
25+
<script>"use strict";
26+
27+
// Configuration
28+
const RGB_ALIVE = 0xD392E6;
29+
const RGB_DEAD = 0xA61B85;
30+
const BIT_ROT = 10;
31+
32+
// Set up the canvas with a 2D rendering context
33+
var cnv = document.getElementsByTagName("canvas")[0];
34+
var ctx = cnv.getContext("2d");
35+
var bcr = cnv.getBoundingClientRect();
36+
37+
// Compute the size of the universe (here: 2px per cell)
38+
var width = bcr.width >>> 1;
39+
var height = bcr.height >>> 1;
40+
var size = width * height;
41+
var byteSize = (size + size) << 2; // input & output (here: 4b per cell)
42+
43+
cnv.width = width;
44+
cnv.height = height;
45+
cnv.style = `
46+
image-rendering: optimizeSpeed;
47+
image-rendering: -moz-crisp-edges;
48+
image-rendering: -webkit-optimize-contrast;
49+
image-rendering: -o-crisp-edges;
50+
image-rendering: optimize-contrast;
51+
image-rendering: crisp-edges;
52+
image-rendering: pixelated;
53+
-ms-interpolation-mode: nearest-neighbor;
54+
`;
55+
ctx.imageSmoothingEnabled = false;
56+
57+
// Compute the size of and instantiate the module's memory
58+
var memory = new WebAssembly.Memory({ initial: ((byteSize + 0xffff) & ~0xffff) >>> 16 });
59+
60+
// Fetch and instantiate the module
61+
fetch("build/optimized.wasm")
62+
.then(response => response.arrayBuffer())
63+
.then(buffer => WebAssembly.instantiate(buffer, {
64+
env: {
65+
memory,
66+
abort: function() {}
67+
},
68+
config: {
69+
BGR_ALIVE : rgb2bgr(RGB_ALIVE) | 1, // little endian, LSB must be set
70+
BGR_DEAD : rgb2bgr(RGB_DEAD) & ~1, // little endian, LSB must not be set
71+
BIT_ROT
72+
},
73+
Math
74+
}))
75+
.then(module => {
76+
var exports = module.instance.exports;
77+
78+
// Initialize the module with the universe's width and height
79+
exports.init(width, height);
80+
81+
var mem = new Uint32Array(memory.buffer);
82+
83+
// Update about 30 times a second
84+
(function update() {
85+
setTimeout(update, 1000 / 30);
86+
mem.copyWithin(0, size, size + size); // copy output to input
87+
exports.step(); // perform the next step
88+
})();
89+
90+
// Keep rendering the output at [size, 2*size]
91+
var imageData = ctx.createImageData(width, height);
92+
var argb = new Uint32Array(imageData.data.buffer);
93+
(function render() {
94+
requestAnimationFrame(render);
95+
argb.set(mem.subarray(size, size + size)); // copy output to image buffer
96+
ctx.putImageData(imageData, 0, 0); // apply image buffer
97+
})();
98+
99+
// When clicked or dragged, fill the current row and column with random live cells
100+
var down = false;
101+
[ [cnv, "mousedown"],
102+
[cnv, "touchstart"]
103+
].forEach(eh => eh[0].addEventListener(eh[1], e => down = true));
104+
[ [document, "mouseup"],
105+
[document, "touchend"]
106+
].forEach(eh => eh[0].addEventListener(eh[1], e => down = false));
107+
[ [cnv, "mousemove"],
108+
[cnv, "touchmove"],
109+
[cnv, "mousedown"]
110+
].forEach(eh => eh[0].addEventListener(eh[1], e => {
111+
if (!down) return;
112+
var loc;
113+
if (e.touches) {
114+
if (e.touches.length > 1) return;
115+
loc = e.touches[0];
116+
} else {
117+
loc = e;
118+
}
119+
var bcr = cnv.getBoundingClientRect();
120+
exports.fill((loc.clientX - bcr.left) >>> 1, (loc.clientY - bcr.top) >>> 1, 0.5);
121+
}));
122+
123+
// :-(
124+
if (navigator.userAgent.indexOf(" Edge/") >= 0) document.getElementById("edge").style.display = "block";
125+
}).catch(err => {
126+
alert("Failed to load WASM: " + err.message + " (ad blocker, maybe?)");
127+
console.log(err.stack);
128+
});
129+
130+
// see comment in assembly/index.ts on why this is useful
131+
function rgb2bgr(rgb) {
132+
return ((rgb >>> 16) & 0xff) | (rgb & 0xff00) | (rgb & 0xff) << 16;
133+
}
134+
</script>
135+
</body>
136+
</html>

game-of-life/package.json

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
{
2+
"name": "@assemblyscript/game-of-life-example",
3+
"version": "1.0.0",
4+
"private": true,
5+
"scripts": {
6+
"asbuild:untouched": "asc assembly/index.ts -b build/untouched.wasm -t build/untouched.wat --use Math=JSMath --runtime none --importMemory --sourceMap --debug --validate --measure",
7+
"asbuild:optimized": "asc assembly/index.ts -b build/optimized.wasm -t build/optimized.wat -d build/optimized.d.ts --use Math=JSMath -O3 --runtime none --importMemory --sourceMap --validate --measure",
8+
"asbuild": "npm run asbuild:untouched && npm run asbuild:optimized",
9+
"start": "http-server . -o -c-1"
10+
},
11+
"devDependencies": {
12+
"assemblyscript": "latest",
13+
"http-server": "^0.12.3"
14+
}
15+
}

game-of-life/preview.jpg

193 KB
Loading

interference/README.md

+27
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
Interference effect
2+
===================
3+
4+
An [AssemblyScript](http://assemblyscript.org) example. Colin Eberhardt's and Ben Smith's [WebAssembly interference effect](https://github.com/ColinEberhardt/wasm-interference), if it was written in AssemblyScript and utilizing 32-bit floating point math.
5+
6+
Instructions
7+
------------
8+
9+
First, install the development dependencies:
10+
11+
```
12+
$> npm install
13+
```
14+
15+
Now, to build [assembly/index.ts](./assembly/index.ts) to an untouched and an optimized `.wasm` including their respective `.wat` representations, run:
16+
17+
```
18+
$> npm run asbuild
19+
```
20+
21+
Afterwards, run
22+
23+
```
24+
$> npm start
25+
```
26+
27+
to start a <a href="http://127.0.0.1:8080">local server</a>. Should also automatically launch a browser.

0 commit comments

Comments
 (0)