├── .gitignore ├── LICENSE ├── Makefile ├── README.md ├── add ├── Makefile ├── README.md ├── index.html ├── main.c └── main.wasm ├── animation ├── Makefile ├── README.md ├── index.html ├── main.c └── main.wasm ├── importexport ├── Makefile ├── README.md ├── index.html ├── main.c └── main.wasm ├── index.html ├── malloc ├── Makefile ├── README.md ├── index.html ├── main.c └── main.wasm ├── memory ├── Makefile ├── README.md ├── index.html ├── main.c └── main.wasm ├── pong ├── Makefile ├── README.md ├── index.html ├── main.c └── main.wasm ├── strings ├── Makefile ├── README.md ├── index.html ├── main.c └── main.wasm ├── tools └── Makefile ├── wat ├── Makefile ├── add.wasm ├── add.wat ├── fibonacci.wasm ├── fibonacci.wat ├── index.html ├── print.wasm └── print.wat └── webgl ├── Makefile ├── README.md ├── index.html ├── main.c └── main.wasm /.gitignore: -------------------------------------------------------------------------------- 1 | tools/bin 2 | tools/build 3 | tools/src 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | TOOLS=$(dir $(lastword $(MAKEFILE_LIST)))/tools 2 | CLANG=$(TOOLS)/bin/bin/clang 3 | LLC=$(TOOLS)/bin/bin/llc 4 | S2WASM=$(TOOLS)/bin/s2wasm 5 | WAST2WASM=$(TOOLS)/bin/bin/wast2wasm 6 | 7 | %.bc : %.c 8 | ${CLANG} --target=wasm32 -Oz -c -emit-llvm $< -o $@ 9 | 10 | %.s : %.bc 11 | ${LLC} $< -o $@ 12 | 13 | %.wat : %.s 14 | ${S2WASM} $< > $@ 15 | 16 | %.wasm : %.wat 17 | ${WAST2WASM} $< -o $@ 18 | 19 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # WASM-Intro 2 | 3 | A scratchpad of stuff in WebAssembly. All examples are written for simplicity, not good code style nor performance. Please do not use anything here in important stuff. 4 | 5 | Much of the toolchain is based on [yurydelendik's "Using WebAssembly in LLVM" gist](https://gist.github.com/yurydelendik/4eeff8248aeb14ce763e). 6 | 7 | ## Build environment 8 | 9 | Run `make build` and `make install` in `tools`. This builds LLVM, clang, binaryen and WABT with correct stuff for WebAssembly compiling. 10 | 11 | ## Examples 12 | 13 | Build by running `make` in each directory. Test using python3 by running `make serve` and browsing to localhost:8000. 14 | 15 | To build intermediate results from the C -> WASM compilation, try `make main.s` and `make main.wat` 16 | 17 | ### Add 18 | 19 | Simple function export example. Adds two numbers together and prints to console 20 | 21 | ### Import/Export 22 | 23 | Same as Add, now with an added step of delegating the addition back to JavaScript. 24 | 25 | ### Memory 26 | 27 | Poking an exported linear memory from JS and C. 28 | 29 | ### Strings 30 | 31 | Manipulating JS strings in C. 32 | 33 | ### Malloc 34 | 35 | Implementing a simple toy dynamic memory allocation scheme. 36 | 37 | ### Animation 38 | 39 | Some canvas functions imported to WASM, shows bouncing particles. 40 | 41 | ### WebGL 42 | 43 | Wraps a portion of WebGL API for importing to WASM, draws a moving triangle. 44 | 45 | ### Pong 46 | 47 | Wraps a little more of WebGL API for importing to WASM, plays Pong. 48 | -------------------------------------------------------------------------------- /add/Makefile: -------------------------------------------------------------------------------- 1 | include ../Makefile 2 | 3 | all: main.wasm 4 | 5 | serve: 6 | python3 -m http.server 7 | -------------------------------------------------------------------------------- /add/README.md: -------------------------------------------------------------------------------- 1 | # wasm-intro/add 2 | 3 | A trivial example of: 4 | 5 | - Defining a WASM function in C 6 | - Loading a WASM module and calling a WASM function from JavaScript 7 | 8 | The page loads the WASM module and calls it to add two integers together. 9 | -------------------------------------------------------------------------------- /add/index.html: -------------------------------------------------------------------------------- 1 | 18 | -------------------------------------------------------------------------------- /add/main.c: -------------------------------------------------------------------------------- 1 | int add(int a, int b) { 2 | return a + b; 3 | } 4 | -------------------------------------------------------------------------------- /add/main.wasm: -------------------------------------------------------------------------------- 1 | asm`pmemoryadd 2 |   j -------------------------------------------------------------------------------- /animation/Makefile: -------------------------------------------------------------------------------- 1 | include ../Makefile 2 | 3 | all: main.wasm 4 | 5 | serve: 6 | python3 -m http.server 7 | -------------------------------------------------------------------------------- /animation/README.md: -------------------------------------------------------------------------------- 1 | # wasm-intro/animation 2 | 3 | A trivial example of: 4 | 5 | - Defining a WASM function in C 6 | - Wrapping Web APIs for calling from WASM 7 | - Animation through onAnimationFrame -> WASM -> Canvas API 8 | - Loading a WASM module, providing external functions and calling a WASM function from JavaScript and vice versa 9 | 10 | The page loads the WASM module and calls it each animation frame to draw bouncing particles on a canvas. 11 | 12 | -------------------------------------------------------------------------------- /animation/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
9 | 10 |
11 | 12 | 49 | 50 | 51 | -------------------------------------------------------------------------------- /animation/main.c: -------------------------------------------------------------------------------- 1 | extern void setFillColor(int, int, int, int); 2 | extern void fillRect(int, int, int, int); 3 | extern int getCanvasWidth(); 4 | extern int getCanvasHeight(); 5 | 6 | typedef struct { 7 | float x; 8 | float y; 9 | float vx; 10 | float vy; 11 | } Particle; 12 | 13 | Particle particles[100]; 14 | int width; 15 | int height; 16 | 17 | void bounce(float* position, float* velocity, float minValue, float maxValue) { 18 | if (*position > maxValue) { 19 | *position -= *position - maxValue; 20 | *velocity = -*velocity; 21 | } else if (*position < minValue) { 22 | *position = minValue + (minValue - *position); 23 | *velocity = -*velocity; 24 | } 25 | } 26 | 27 | void onInit() { 28 | width = getCanvasWidth(); 29 | height = getCanvasHeight(); 30 | for (int i = 0; i < 100; ++i) { 31 | particles[i].x = width/2.0; 32 | particles[i].y = height/2.0; 33 | particles[i].vx = ((i*7 % 200) - 100)/100.0; 34 | particles[i].vy = ((i*19 % 200) - 100)/100.0; 35 | } 36 | } 37 | 38 | void onAnimationFrame(int timestamp) { 39 | static int previous = 0; 40 | 41 | int delta = previous ? timestamp - previous : 0; 42 | 43 | setFillColor(64,64,64,255); 44 | fillRect(0, 0, width, height); 45 | 46 | setFillColor(255,0,0,255); 47 | for (int i = 0; i < 100; ++i) { 48 | particles[i].vy += 0.001 * delta; 49 | particles[i].x += particles[i].vx * delta; 50 | particles[i].y += particles[i].vy * delta; 51 | 52 | bounce(&particles[i].x, &particles[i].vx, 0, width - 10); 53 | bounce(&particles[i].y, &particles[i].vy, 0, height - 10); 54 | 55 | fillRect((int) (particles[i].x + 0.5), (int) (particles[i].y + 0.5), 10, 10); 56 | } 57 | 58 | previous = timestamp; 59 | } 60 | -------------------------------------------------------------------------------- /animation/main.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bzar/wasm-intro/d53c653b35649f9433ef5e7f5fff9da49483221b/animation/main.wasm -------------------------------------------------------------------------------- /importexport/Makefile: -------------------------------------------------------------------------------- 1 | include ../Makefile 2 | 3 | all: main.wasm 4 | 5 | serve: 6 | python3 -m http.server 7 | -------------------------------------------------------------------------------- /importexport/README.md: -------------------------------------------------------------------------------- 1 | # wasm-intro/importexport 2 | 3 | A trivial example of: 4 | 5 | - Defining a WASM function in C 6 | - Defining an external function callable from C 7 | - Loading a WASM module, providing external functions and calling a WASM function from JavaScript 8 | 9 | The page loads the WASM module and calls it to call a JavaScript function to add two integers together. 10 | -------------------------------------------------------------------------------- /importexport/index.html: -------------------------------------------------------------------------------- 1 | 21 | -------------------------------------------------------------------------------- /importexport/main.c: -------------------------------------------------------------------------------- 1 | extern int js_add(int, int); 2 | 3 | 4 | int add(int a, int b) { 5 | return js_add(a, b); 6 | } 7 | -------------------------------------------------------------------------------- /importexport/main.wasm: -------------------------------------------------------------------------------- 1 | asm`envjs_addpmemoryadd 2 | 3 |   -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /malloc/Makefile: -------------------------------------------------------------------------------- 1 | include ../Makefile 2 | 3 | all: main.wasm 4 | 5 | serve: 6 | python3 -m http.server 7 | -------------------------------------------------------------------------------- /malloc/README.md: -------------------------------------------------------------------------------- 1 | # wasm-intro/malloc 2 | 3 | A trivial example of: 4 | 5 | - Defining a WASM function in C 6 | - Accessing WASM linear memory from both JavaScript and C 7 | - Implementing a simple toy malloc/free/sizeof toolchain to manage dynamic memory allocation 8 | - Loading a WASM module and calling a WASM function from JavaScript 9 | 10 | The page loads the WASM module, then allocates WASM linear memory using a custom malloc implementation to store strings, which are then read and printed with the help of msize. Next the middle one is freed using mfree, and its memory block is reused for another smaller string allocation. 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /malloc/index.html: -------------------------------------------------------------------------------- 1 | 65 | -------------------------------------------------------------------------------- /malloc/main.c: -------------------------------------------------------------------------------- 1 | typedef struct Block { 2 | unsigned int size; 3 | unsigned int usize; 4 | struct Block* next; 5 | struct Block* prev; 6 | } Block; 7 | 8 | Block* usedBlocks = 0; 9 | Block* freeBlocks = 0; 10 | Block* nextBlock = (Block*) 1024; 11 | 12 | void* malloc(unsigned int size) { 13 | Block* b = freeBlocks; 14 | while (b && b->size < size) b = b->next; 15 | 16 | if(!b) { 17 | b = nextBlock; 18 | b->size = size; 19 | nextBlock += size + sizeof(Block); // Header + data 20 | } 21 | 22 | b->usize = size; 23 | b->next = usedBlocks; 24 | if(b->next) b->next->prev = b; 25 | b->prev = 0; 26 | usedBlocks = b; 27 | 28 | return (void*) b + sizeof(Block); 29 | } 30 | 31 | void mfree(void* data) { 32 | Block* b = data - sizeof(Block); 33 | if(b->prev) b->prev->next = b->next; 34 | b->next = freeBlocks; 35 | if(b->next) b->next->prev = b; 36 | b->prev = 0; 37 | freeBlocks = b; 38 | } 39 | 40 | unsigned int msize(void* data) { 41 | Block* b = data - sizeof(Block); 42 | return b->usize; 43 | } 44 | -------------------------------------------------------------------------------- /malloc/main.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bzar/wasm-intro/d53c653b35649f9433ef5e7f5fff9da49483221b/malloc/main.wasm -------------------------------------------------------------------------------- /memory/Makefile: -------------------------------------------------------------------------------- 1 | include ../Makefile 2 | 3 | all: main.wasm 4 | 5 | serve: 6 | python3 -m http.server 7 | -------------------------------------------------------------------------------- /memory/README.md: -------------------------------------------------------------------------------- 1 | # wasm-intro/memory 2 | 3 | A trivial example of: 4 | 5 | - Defining a WASM function in C 6 | - Defining external functions callable from C 7 | - Accessing WASM linear memory from both JavaScript and C 8 | - Loading a WASM module and calling a WASM function from JavaScript 9 | 10 | The page loads the WASM module, then manipulates and reads its exported memory using both JavaScript typed arrays and C pointers. 11 | 12 | -------------------------------------------------------------------------------- /memory/index.html: -------------------------------------------------------------------------------- 1 | 31 | -------------------------------------------------------------------------------- /memory/main.c: -------------------------------------------------------------------------------- 1 | unsigned int* memory = 0; 2 | 3 | unsigned int put(unsigned int offset, unsigned int value) { 4 | *(memory + offset) = value; 5 | return *(memory + offset); 6 | } 7 | 8 | unsigned int get(unsigned int offset) { 9 | return *(memory + offset); 10 | } 11 | -------------------------------------------------------------------------------- /memory/main.wasm: -------------------------------------------------------------------------------- 1 | asm ``pmemoryputget 2 | 'A( Atj 6  A( Atj( 3 | A  -------------------------------------------------------------------------------- /pong/Makefile: -------------------------------------------------------------------------------- 1 | include ../Makefile 2 | 3 | all: main.wasm 4 | 5 | serve: 6 | python3 -m http.server 7 | -------------------------------------------------------------------------------- /pong/README.md: -------------------------------------------------------------------------------- 1 | # wasm-intro/pong 2 | 3 | A non-trivial example of: 4 | 5 | - Defining a WASM function in C 6 | - Wrapping WebGL API for calling from WASM 7 | - Wrapping Web Audio API for calling from WASM 8 | - Animation through onAnimationFrame -> WASM -> Canvas API 9 | - Loading a WASM module, providing external functions and calling a WASM function from JavaScript and vice versa 10 | 11 | The page loads the WASM module and calls it each animation frame and input event to play pong on a canvas using WebGL. The game also generates sound effects in WASM to play through an AudioBuffer. 12 | 13 | -------------------------------------------------------------------------------- /pong/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
9 |
10 | 0 11 | 0 12 |
13 | 14 |
15 | 16 | 146 | 147 | 148 | -------------------------------------------------------------------------------- /pong/main.c: -------------------------------------------------------------------------------- 1 | extern unsigned int compileShader(char const* source, unsigned int len, unsigned int type); 2 | extern unsigned int linkShaderProgram(unsigned int vertexShaderId, unsigned int fragmentShaderId); 3 | 4 | extern void glClearColor(float, float, float, float); 5 | extern void glEnable(unsigned int); 6 | extern void glDepthFunc(unsigned int); 7 | extern void glBlendFunc(unsigned int, unsigned int); 8 | extern void glClear(unsigned int); 9 | extern int glGetAttribLocation(unsigned int, char const*, unsigned int); 10 | extern int glGetUniformLocation(unsigned int, char const*, unsigned int); 11 | extern void glUniform4fv(int, float, float, float, float); 12 | extern void glUniform1i(int, int); 13 | extern void glUniform1f(int, float); 14 | extern unsigned int glCreateBuffer(); 15 | extern void glBindBuffer(unsigned int, unsigned int); 16 | extern void glBufferData(unsigned int, float const*, unsigned int, unsigned int); 17 | extern void glUseProgram(unsigned int); 18 | extern void glEnableVertexAttribArray(unsigned int); 19 | extern void glVertexAttribPointer(unsigned int, unsigned int, unsigned int, unsigned int, unsigned int, unsigned int); 20 | extern void glDrawArrays(unsigned int, unsigned int, unsigned int); 21 | extern unsigned int glCreateTexture(); 22 | extern void glBindTexture(unsigned int, unsigned int); 23 | extern void glTexImage2D(unsigned int, unsigned int, unsigned int, unsigned int, unsigned int, unsigned int, unsigned int, unsigned int, unsigned char const*, unsigned int); 24 | extern void glTexParameteri(unsigned int, unsigned int, unsigned int); 25 | extern void glActiveTexture(unsigned int); 26 | 27 | extern void consoleLog(unsigned int); 28 | extern void playAudio(float*, unsigned int); 29 | extern void setScore(unsigned int, unsigned int); 30 | 31 | // Identifier constants pulled from WebGLRenderingContext 32 | unsigned int const GL_VERTEX_SHADER = 35633; 33 | unsigned int const GL_FRAGMENT_SHADER = 35632; 34 | unsigned int const GL_ARRAY_BUFFER = 34962; 35 | unsigned int const GL_TRIANGLES = 4; 36 | unsigned int const GL_STATIC_DRAW = 35044; 37 | unsigned int const GL_FLOAT = 5126; 38 | unsigned int const GL_DEPTH_TEST = 2929; 39 | unsigned int const GL_LEQUAL = 515; 40 | unsigned int const GL_COLOR_BUFFER_BIT = 16384; 41 | unsigned int const GL_DEPTH_BUFFER_BIT = 256; 42 | unsigned int const GL_TEXTURE_2D = 3553; 43 | unsigned int const GL_RGBA = 6408; 44 | unsigned int const GL_UNSIGNED_BYTE = 5121; 45 | unsigned int const GL_TEXTURE_MAG_FILTER = 10240; 46 | unsigned int const GL_TEXTURE_MIN_FILTER = 10241; 47 | unsigned int const GL_NEAREST = 9728; 48 | unsigned int const GL_TEXTURE0 = 33984; 49 | unsigned int const GL_BLEND = 3042; 50 | unsigned int const GL_SRC_ALPHA = 770; 51 | unsigned int const GL_ONE_MINUS_SRC_ALPHA = 771; 52 | unsigned int const GL_ONE= 1; 53 | 54 | 55 | 56 | char const vertexShader[] = 57 | "attribute vec2 a_position;" 58 | "attribute vec2 a_texcoord;" 59 | "uniform vec4 u_offset;" 60 | "varying mediump vec2 v_texcoord;" 61 | "void main() {" 62 | " gl_Position = vec4(a_position, 1.0, 1.0) + u_offset;" 63 | " v_texcoord = a_texcoord;" 64 | "}"; 65 | 66 | char const fragmentShader[] = 67 | "precision mediump float;" 68 | "varying mediump vec2 v_texcoord;" 69 | "uniform sampler2D u_sampler;" 70 | "uniform float u_opacity;" 71 | "void main() {" 72 | " gl_FragColor = texture2D(u_sampler, v_texcoord);" 73 | " gl_FragColor.a *= u_opacity;" 74 | "}"; 75 | 76 | float const BALL_VERTICES[] = { 77 | -0.05,-0.05,0,0, 0.05,0.05,1,1, -0.05,0.05,0,1, 78 | -0.05,-0.05,0,0, 0.05,-0.05,1,0, 0.05,0.05,1,1 79 | }; 80 | 81 | unsigned char const BALL_TEXTURE[] = { 82 | 0x00,0x00,0x00,0x00, 0xDD,0x00,0x00,0xFF, 0xDD,0x00,0x00,0xFF, 0x00,0x00,0x00,0x00, 83 | 0xDD,0x00,0x00,0xFF, 0xFF,0x00,0x00,0xFF, 0xFF,0x44,0x44,0xFF, 0xDD,0x00,0x00,0xFF, 84 | 0xDD,0x00,0x00,0xFF, 0xFF,0x44,0x44,0xFF, 0xFF,0x88,0x88,0xFF, 0xDD,0x00,0x00,0xFF, 85 | 0x00,0x00,0x00,0x00, 0xDD,0x00,0x00,0xFF, 0xDD,0x00,0x00,0xFF, 0x00,0x00,0x00,0x00 86 | }; 87 | float const BALL_TAIL_VERTICES[] = { 88 | -0.04,-0.04,0,0, 0.04,0.04,1,1, -0.04,0.04,0,1, 89 | -0.04,-0.04,0,0, 0.04,-0.04,1,0, 0.04,0.04,1,1 90 | }; 91 | 92 | unsigned char const BALL_TAIL_TEXTURE[] = { 93 | 0x00,0x00,0x00,0x00, 0xFF,0x00,0x00,0xCC, 0xFF,0x00,0x00,0xCC, 0x00,0x00,0x00,0x00, 94 | 0xFF,0x00,0x00,0xCC, 0xFF,0x00,0x00,0xCC, 0xFF,0x00,0x00,0xCC, 0xFF,0x00,0x00,0xCC, 95 | 0xFF,0x00,0x00,0xCC, 0xFF,0x00,0x00,0xCC, 0xFF,0x00,0x00,0xCC, 0xFF,0x00,0x00,0xCC, 96 | 0x00,0x00,0x00,0x00, 0xFF,0x00,0x00,0xCC, 0xFF,0x00,0x00,0xCC, 0x00,0x00,0x00,0x00 97 | }; 98 | 99 | float const PADDLE_VERTICES[] = { 100 | -0.05,-0.20,0,0, 0.05,0.20,1,1, -0.05,0.20,0,1, 101 | -0.05,-0.20,0,0, 0.05,-0.20,1,0, 0.05,0.20,1,1 102 | }; 103 | 104 | unsigned char const PADDLE_TEXTURE[] = { 105 | 0x00,0xDD,0x00,0xFF, 0x00,0xEE,0x00,0xFF, 0x00,0xFF,0x00,0xFF, 0x00,0xFF,0x00,0xFF, 0x00,0xFF,0x00,0xFF, 0x00,0xFF,0x00,0xFF, 0x00,0xEE,0x00,0xFF, 0x00,0xDD,0x00,0xFF, 106 | 0x00,0xDD,0x00,0xFF, 0x00,0xEE,0x00,0xFF, 0x00,0xFF,0x00,0xFF, 0x00,0xFF,0x00,0xFF, 0x00,0xFF,0x00,0xFF, 0x00,0xFF,0x00,0xFF, 0x00,0xEE,0x00,0xFF, 0x00,0xDD,0x00,0xFF, 107 | 0x00,0xDD,0x00,0xFF, 0x00,0xEE,0x00,0xFF, 0x00,0xFF,0x00,0xFF, 0x00,0xFF,0x00,0xFF, 0x00,0xFF,0x00,0xFF, 0x00,0xFF,0x00,0xFF, 0x00,0xEE,0x00,0xFF, 0x00,0xDD,0x00,0xFF, 108 | 0x00,0xDD,0x00,0xFF, 0x00,0xEE,0x00,0xFF, 0x00,0xFF,0x00,0xFF, 0x00,0xFF,0x00,0xFF, 0x00,0xFF,0x00,0xFF, 0x00,0xFF,0x00,0xFF, 0x00,0xEE,0x00,0xFF, 0x00,0xDD,0x00,0xFF, 109 | 0x00,0xDD,0x00,0xFF, 0x00,0xEE,0x00,0xFF, 0x00,0xFF,0x00,0xFF, 0x00,0xFF,0x00,0xFF, 0x00,0xFF,0x00,0xFF, 0x00,0xFF,0x00,0xFF, 0x00,0xEE,0x00,0xFF, 0x00,0xDD,0x00,0xFF, 110 | 0x00,0xDD,0x00,0xFF, 0x00,0xEE,0x00,0xFF, 0x00,0xFF,0x00,0xFF, 0x00,0xFF,0x00,0xFF, 0x00,0xFF,0x00,0xFF, 0x00,0xFF,0x00,0xFF, 0x00,0xEE,0x00,0xFF, 0x00,0xDD,0x00,0xFF, 111 | 0x00,0xDD,0x00,0xFF, 0x00,0xEE,0x00,0xFF, 0x00,0xFF,0x00,0xFF, 0x00,0xFF,0x00,0xFF, 0x00,0xFF,0x00,0xFF, 0x00,0xFF,0x00,0xFF, 0x00,0xEE,0x00,0xFF, 0x00,0xDD,0x00,0xFF, 112 | 0x00,0xDD,0x00,0xFF, 0x00,0xEE,0x00,0xFF, 0x00,0xFF,0x00,0xFF, 0x00,0xFF,0x00,0xFF, 0x00,0xFF,0x00,0xFF, 0x00,0xFF,0x00,0xFF, 0x00,0xEE,0x00,0xFF, 0x00,0xDD,0x00,0xFF, 113 | }; 114 | 115 | float const FIELD_VERTICES[] = { 116 | -1,-1,0,0, 1,1,1,1, -1,1,0,1, 117 | -1,-1,0,0, 1,-1,1,0, 1,1,1,1 118 | }; 119 | 120 | unsigned char const FIELD_TEXTURE[] = { 121 | 0x33,0x33,0x33,0xFF, 0x22,0x22,0x22,0xFF, 0x11,0x11,0x11,0xFF, 0x66,0x66,0x66,0xFF, 0x55,0x55,0x55,0xFF, 0x11,0x11,0x11,0xFF, 0x22,0x22,0x22,0xFF, 0x33,0x33,0x33,0xFF, 122 | 0x33,0x33,0x33,0xFF, 0x22,0x22,0x22,0xFF, 0x11,0x11,0x11,0xFF, 0x55,0x55,0x55,0xFF, 0x66,0x66,0x66,0xFF, 0x11,0x11,0x11,0xFF, 0x22,0x22,0x22,0xFF, 0x33,0x33,0x33,0xFF, 123 | 0x33,0x33,0x33,0xFF, 0x22,0x22,0x22,0xFF, 0x11,0x11,0x11,0xFF, 0x66,0x66,0x66,0xFF, 0x55,0x55,0x55,0xFF, 0x11,0x11,0x11,0xFF, 0x22,0x22,0x22,0xFF, 0x33,0x33,0x33,0xFF, 124 | 0x33,0x33,0x33,0xFF, 0x22,0x22,0x22,0xFF, 0x11,0x11,0x11,0xFF, 0x55,0x55,0x55,0xFF, 0x66,0x66,0x66,0xFF, 0x11,0x11,0x11,0xFF, 0x22,0x22,0x22,0xFF, 0x33,0x33,0x33,0xFF, 125 | 0x33,0x33,0x33,0xFF, 0x22,0x22,0x22,0xFF, 0x11,0x11,0x11,0xFF, 0x66,0x66,0x66,0xFF, 0x55,0x55,0x55,0xFF, 0x11,0x11,0x11,0xFF, 0x22,0x22,0x22,0xFF, 0x33,0x33,0x33,0xFF, 126 | 0x33,0x33,0x33,0xFF, 0x22,0x22,0x22,0xFF, 0x11,0x11,0x11,0xFF, 0x55,0x55,0x55,0xFF, 0x66,0x66,0x66,0xFF, 0x11,0x11,0x11,0xFF, 0x22,0x22,0x22,0xFF, 0x33,0x33,0x33,0xFF, 127 | 0x33,0x33,0x33,0xFF, 0x22,0x22,0x22,0xFF, 0x11,0x11,0x11,0xFF, 0x66,0x66,0x66,0xFF, 0x55,0x55,0x55,0xFF, 0x11,0x11,0x11,0xFF, 0x22,0x22,0x22,0xFF, 0x33,0x33,0x33,0xFF, 128 | 0x33,0x33,0x33,0xFF, 0x22,0x22,0x22,0xFF, 0x11,0x11,0x11,0xFF, 0x55,0x55,0x55,0xFF, 0x66,0x66,0x66,0xFF, 0x11,0x11,0x11,0xFF, 0x22,0x22,0x22,0xFF, 0x33,0x33,0x33,0xFF, 129 | }; 130 | 131 | float const SPARK_VERTICES[] = { 132 | -0.01,-0.01,0,0, 0.01,0.01,1,1, -0.01,0.01,0,1, 133 | -0.01,-0.01,0,0, 0.01,-0.01,1,0, 0.01,0.01,1,1 134 | }; 135 | 136 | unsigned char const SPARK_TEXTURE[] = { 137 | 0x00,0x00,0x00,0x00, 0xFF,0xFF,0x00,0xFF, 0xFF,0xFF,0x00,0xFF, 0x00,0x00,0x00,0x00, 138 | 0xFF,0xFF,0x00,0xFF, 0xFF,0xFF,0x88,0xFF, 0xFF,0xFF,0x88,0xFF, 0xFF,0xFF,0x00,0xFF, 139 | 0xFF,0xFF,0x00,0xFF, 0xFF,0xFF,0x88,0xFF, 0xFF,0xFF,0x88,0xFF, 0xFF,0xFF,0x00,0xFF, 140 | 0x00,0x00,0x00,0x00, 0xFF,0xFF,0x00,0xFF, 0xFF,0xFF,0x00,0xFF, 0x00,0x00,0x00,0x00, 141 | }; 142 | 143 | unsigned int const AUDIO_BUFFER_SIZE = 8192; 144 | float beep[AUDIO_BUFFER_SIZE] = { 0 }; 145 | float boop[AUDIO_BUFFER_SIZE] = { 0 }; 146 | float bloop[AUDIO_BUFFER_SIZE] = { 0 }; 147 | 148 | float const PADDLE_SPEED = 0.001; 149 | float const BALL_SPEED = 0.0012; 150 | 151 | typedef struct Vec2 { 152 | float x; 153 | float y; 154 | } Vec2; 155 | 156 | typedef struct Model { 157 | unsigned int vertexBufferId; 158 | unsigned int numVertices; 159 | unsigned int textureId; 160 | Vec2 extent; 161 | } Model; 162 | 163 | typedef struct Sprite { 164 | Vec2 position; 165 | Model* model; 166 | } Sprite; 167 | 168 | typedef struct Paddle { 169 | Sprite sprite; 170 | char up; 171 | char down; 172 | } Paddle; 173 | 174 | typedef struct Ball { 175 | Sprite sprite; 176 | Vec2 velocity; 177 | } Ball; 178 | 179 | typedef struct Particle { 180 | Vec2 position; 181 | Vec2 velocity; 182 | Vec2 acceleration; 183 | unsigned int life; 184 | unsigned int totalLife; 185 | } Particle; 186 | 187 | unsigned int const MAX_PARTICLES = 100; 188 | 189 | typedef struct ParticleSystem { 190 | Particle particles[MAX_PARTICLES]; 191 | Model* model; 192 | unsigned int alive; 193 | } ParticleSystem; 194 | 195 | void createParticle(ParticleSystem*, float x, float y, float vx, float vy, float ax, float ay, int life); 196 | void updateParticleSystem(ParticleSystem*, unsigned int); 197 | void renderParticleSystem(ParticleSystem const*); 198 | 199 | unsigned int programId; 200 | int positionAttributeLocation; 201 | int texcoordAttributeLocation; 202 | int offsetUniformLocation; 203 | int samplerUniformLocation; 204 | int opacityUniformLocation; 205 | unsigned int positionBuffer; 206 | 207 | Model ballModel = {0, 0, 0, {}}; 208 | Model ballTailModel = {0, 0, 0, {}}; 209 | Model paddleModel = {0, 0, 0, {}}; 210 | Model fieldModel = {0, 0, 0, {}}; 211 | Model sparkModel = {0, 0, 0, {}}; 212 | 213 | Ball ball = { { {0, 0}, &ballModel }, {1, 1} }; 214 | Paddle left = { { {-0.9, 0}, &paddleModel }, 0, 0}; 215 | Paddle right = { { {0.9, 0}, &paddleModel }, 0, 0}; 216 | Sprite field = { {0, 0}, &fieldModel }; 217 | ParticleSystem sparks = { { { {0,0}, {0,0}, {0,0}, 0, 0 } }, &sparkModel, 0 }; 218 | ParticleSystem ballTail = { { { {0,0}, {0,0}, {0,0}, 0, 0 } }, &ballTailModel, 0 }; 219 | 220 | unsigned int leftScore = 0; 221 | unsigned int rightScore = 0; 222 | 223 | unsigned int initTexture(unsigned char const* data, unsigned int width, unsigned int height) { 224 | unsigned int id = glCreateTexture(); 225 | glBindTexture(GL_TEXTURE_2D, id); 226 | glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, data, width * height * sizeof(float)); 227 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); 228 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); 229 | 230 | return id; 231 | } 232 | void initModel(Model* m, float const vertices[], unsigned int numVertices, unsigned int textureId) { 233 | m->vertexBufferId = glCreateBuffer(); 234 | m->numVertices = numVertices; 235 | m->textureId = textureId; 236 | glBindBuffer(GL_ARRAY_BUFFER, m->vertexBufferId); 237 | glBufferData(GL_ARRAY_BUFFER, vertices, numVertices, GL_STATIC_DRAW); 238 | 239 | for(unsigned int i = 0; i < numVertices; i += 4) { 240 | float fx = vertices[i]; 241 | float fy = vertices[i+1]; 242 | m->extent.x = fx > m->extent.x ? fx : -fx > m->extent.x ? -fx : m->extent.x; 243 | m->extent.y = fy > m->extent.y ? fy : -fy > m->extent.y ? -fy : m->extent.y; 244 | } 245 | m->extent.x *= 0.9; 246 | m->extent.y *= 0.9; 247 | } 248 | 249 | void onInit() { 250 | glClearColor(0.1, 0.1, 0.1, 1.0); 251 | glEnable(GL_DEPTH_TEST); 252 | glEnable(GL_BLEND); 253 | glDepthFunc(GL_LEQUAL); 254 | glBlendFunc(GL_SRC_ALPHA, GL_ONE); 255 | glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); 256 | 257 | unsigned int vsId = compileShader(vertexShader, sizeof(vertexShader) - 1, GL_VERTEX_SHADER); 258 | unsigned int fsId = compileShader(fragmentShader, sizeof(fragmentShader) - 1, GL_FRAGMENT_SHADER); 259 | 260 | programId = linkShaderProgram(vsId, fsId); 261 | 262 | positionAttributeLocation = glGetAttribLocation(programId, "a_position", sizeof("a_position") - 1); 263 | texcoordAttributeLocation = glGetAttribLocation(programId, "a_texcoord", sizeof("a_texcoord") - 1); 264 | offsetUniformLocation = glGetUniformLocation(programId, "u_offset", sizeof("u_offset") - 1); 265 | samplerUniformLocation = glGetUniformLocation(programId, "u_sampler", sizeof("u_sampler") - 1); 266 | opacityUniformLocation = glGetUniformLocation(programId, "u_opacity", sizeof("u_opacity") - 1); 267 | 268 | unsigned int ballTextureId = initTexture(BALL_TEXTURE, 4, 4); 269 | unsigned int ballTailTextureId = initTexture(BALL_TAIL_TEXTURE, 4, 4); 270 | unsigned int paddleTextureId = initTexture(PADDLE_TEXTURE, 8, 8); 271 | unsigned int fieldTextureId = initTexture(FIELD_TEXTURE, 8, 8); 272 | unsigned int sparkTextureId = initTexture(SPARK_TEXTURE, 4, 4); 273 | initModel(&ballModel, BALL_VERTICES, sizeof(BALL_VERTICES)/sizeof(float), ballTextureId); 274 | initModel(&ballTailModel, BALL_TAIL_VERTICES, sizeof(BALL_TAIL_VERTICES)/sizeof(float), ballTailTextureId); 275 | initModel(&paddleModel, PADDLE_VERTICES, sizeof(PADDLE_VERTICES)/sizeof(float), paddleTextureId); 276 | initModel(&fieldModel, FIELD_VERTICES, sizeof(FIELD_VERTICES)/sizeof(float), fieldTextureId); 277 | initModel(&sparkModel, SPARK_VERTICES, sizeof(SPARK_VERTICES)/sizeof(float), sparkTextureId); 278 | 279 | for(unsigned int i = 0; i < AUDIO_BUFFER_SIZE; ++i) { 280 | beep[i] = (i/64)%2 ? 0.1 : -0.1; 281 | boop[i] = (i/128)%2 ? 0.1 : -0.1; 282 | bloop[i] = beep[i]/2 + boop[i]/2; 283 | } 284 | } 285 | 286 | void renderSprites(Sprite* const* ss, unsigned int n) { 287 | glBindBuffer(GL_ARRAY_BUFFER, ss[0]->model->vertexBufferId); 288 | glActiveTexture(GL_TEXTURE0); 289 | glBindTexture(GL_TEXTURE_2D, ss[0]->model->textureId); 290 | glVertexAttribPointer(positionAttributeLocation, 2, GL_FLOAT, 0, 16, 0); 291 | glVertexAttribPointer(texcoordAttributeLocation, 2, GL_FLOAT, 0, 16, 8); 292 | glUniform1i(samplerUniformLocation, 0); 293 | glUniform1f(opacityUniformLocation, 1.0f); 294 | 295 | unsigned int numTriangles = ss[0]->model->numVertices / 4; 296 | for(unsigned int i = 0; i < n; ++i) { 297 | Sprite* s = ss[i]; 298 | glUniform4fv(offsetUniformLocation, s->position.x, s->position.y, 0.0f, 0.0f); 299 | glDrawArrays(GL_TRIANGLES, 0, numTriangles); 300 | } 301 | } 302 | 303 | float clamp(float v, float min, float max) { 304 | return v < min ? min : v > max ? max : v; 305 | } 306 | 307 | char spriteCollide(Sprite* a, Sprite* b) { 308 | return (a->position.x < b->position.x ? b->position.x - a->position.x : a->position.x - b->position.x) < a->model->extent.x + b->model->extent.x && 309 | (a->position.y < b->position.y ? b->position.y - a->position.y : a->position.y - b->position.y) < a->model->extent.y + b->model->extent.y; 310 | } 311 | 312 | void createSparks(ParticleSystem* ps, float x, float y, float dx, float dy) { 313 | for(unsigned int i = 0; i < 4; ++i) { 314 | float ddx = (i+1)*dx/10.0f; 315 | float ddy = (i+1)*dy/10.0f; 316 | createParticle(ps, x, y, dy+ddx, -dx+ddy, 0, 0, 100); 317 | createParticle(ps, x, y, -dy+ddx, dx+ddy, 0, 0, 100); 318 | } 319 | } 320 | 321 | void onAnimationFrame(int timestamp) { 322 | static int previous = 0; 323 | 324 | int delta = previous ? timestamp - previous : 0; 325 | 326 | left.sprite.position.y = clamp(left.sprite.position.y + (left.up - left.down) * PADDLE_SPEED * delta, -0.8, 0.8); 327 | right.sprite.position.y = clamp(right.sprite.position.y + (right.up - right.down) * PADDLE_SPEED * delta, -0.8, 0.8); 328 | 329 | ball.sprite.position.x += ball.velocity.x * BALL_SPEED * delta; 330 | if(spriteCollide(&ball.sprite, &left.sprite) || spriteCollide(&ball.sprite, &right.sprite)) { 331 | ball.velocity.x = -ball.velocity.x; 332 | ball.sprite.position.x += ball.velocity.x * BALL_SPEED * delta; 333 | createSparks(&sparks, 334 | (ball.velocity.x > 0 ? -1 : 1) * ball.sprite.model->extent.x + ball.sprite.position.x, 335 | ball.sprite.position.y, 2*ball.velocity.x, 0); 336 | playAudio(beep, AUDIO_BUFFER_SIZE); 337 | } else if(ball.sprite.position.x > 1.05) { 338 | ball.sprite.position.x = 0; 339 | ball.velocity.x = 1 - 2 * (timestamp % 2); 340 | ball.velocity.y = 1 - 2 * ((timestamp/7) % 2); 341 | leftScore += 1; 342 | setScore(0, leftScore); 343 | playAudio(bloop, AUDIO_BUFFER_SIZE); 344 | } else if(ball.sprite.position.x < -1.05) { 345 | ball.sprite.position.x = 0; 346 | ball.velocity.x = 1 - 2 * (timestamp % 2); 347 | ball.velocity.y = 1 - 2 * ((timestamp/7) % 2); 348 | rightScore += 1; 349 | setScore(1, rightScore); 350 | playAudio(bloop, AUDIO_BUFFER_SIZE); 351 | } 352 | 353 | ball.sprite.position.y += ball.velocity.y * BALL_SPEED * delta; 354 | if(spriteCollide(&ball.sprite, &left.sprite) || spriteCollide(&ball.sprite, &right.sprite)) { 355 | ball.velocity.y = -ball.velocity.y; 356 | ball.sprite.position.y += ball.velocity.y * BALL_SPEED * delta; 357 | createSparks(&sparks, ball.sprite.position.x, 358 | (ball.velocity.y > 0 ? -1 : 1) * ball.sprite.model->extent.y + ball.sprite.position.y, 359 | 0, 2*ball.velocity.y); 360 | playAudio(beep, AUDIO_BUFFER_SIZE); 361 | } else if(ball.sprite.position.y > 0.95) { 362 | ball.velocity.y = -1; 363 | createSparks(&sparks, ball.sprite.position.x, 364 | ball.sprite.model->extent.y + ball.sprite.position.y, 365 | 0, 2*ball.velocity.y); 366 | playAudio(boop, AUDIO_BUFFER_SIZE); 367 | } else if(ball.sprite.position.y < -0.95) { 368 | ball.velocity.y = 1; 369 | createSparks(&sparks, ball.sprite.position.x, 370 | -ball.sprite.model->extent.y + ball.sprite.position.y, 371 | 0, 2*ball.velocity.y); 372 | playAudio(boop, AUDIO_BUFFER_SIZE); 373 | } 374 | 375 | createParticle(&ballTail, ball.sprite.position.x, ball.sprite.position.y,0,0, 0, 0, 1000); 376 | 377 | updateParticleSystem(&sparks, delta); 378 | updateParticleSystem(&ballTail, delta); 379 | 380 | glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); 381 | 382 | glUseProgram(programId); 383 | glEnableVertexAttribArray(positionAttributeLocation); 384 | glEnableVertexAttribArray(texcoordAttributeLocation); 385 | Sprite* const fields[] = {&field}; 386 | renderSprites(fields, 1); 387 | Sprite* const paddles[] = {&left.sprite, &right.sprite}; 388 | renderSprites(paddles, 2); 389 | renderParticleSystem(&ballTail); 390 | Sprite* const balls[] = {&ball.sprite}; 391 | renderSprites(balls, 1); 392 | 393 | renderParticleSystem(&sparks); 394 | 395 | previous = timestamp; 396 | } 397 | 398 | unsigned int const KEY_UP = 38; 399 | unsigned int const KEY_DOWN = 40; 400 | unsigned int const KEY_A = 65; 401 | unsigned int const KEY_Z = 90; 402 | 403 | void onKey(unsigned int keyCode, char state) { 404 | switch(keyCode) { 405 | case KEY_UP: 406 | right.up = state; 407 | break; 408 | case KEY_DOWN: 409 | right.down = state; 410 | break; 411 | case KEY_A: 412 | left.up = state; 413 | break; 414 | case KEY_Z: 415 | left.down = state; 416 | break; 417 | default: 418 | break; 419 | } 420 | } 421 | 422 | void createParticle(ParticleSystem* ps, float x, float y, float vx, float vy, float ax, float ay, int life) { 423 | if(ps->alive < MAX_PARTICLES) { 424 | Particle* np = &ps->particles[ps->alive]; 425 | np->position.x = x; 426 | np->position.y = y; 427 | np->velocity.x = vx; 428 | np->velocity.y = vy; 429 | np->acceleration.x = ax; 430 | np->acceleration.y = ay; 431 | np->life = life; 432 | np->totalLife = life; 433 | ps->alive += 1; 434 | } 435 | } 436 | 437 | void updateParticleSystem(ParticleSystem* ps, unsigned int delta) { 438 | for(unsigned int i = 0; i < ps->alive; ++i) { 439 | Particle* p = &ps->particles[i]; 440 | if(delta > p->life) { 441 | ps->alive -= 1; 442 | if(i < ps->alive) { 443 | Particle* np = &ps->particles[ps->alive]; 444 | p->position = np->position; 445 | p->velocity = np->velocity; 446 | p->acceleration = np->acceleration; 447 | p->life = np->life; 448 | p->totalLife = np->totalLife; 449 | i -= 1; 450 | } 451 | } else { 452 | p->life -= delta; 453 | p->velocity.x += p->acceleration.x * delta / 1000.0f; 454 | p->velocity.y += p->acceleration.y * delta / 1000.0f; 455 | p->position.x += p->velocity.x * delta / 1000.0f; 456 | p->position.y += p->velocity.y * delta / 1000.0f; 457 | } 458 | } 459 | } 460 | 461 | void renderParticleSystem(ParticleSystem const* ps) { 462 | glBindBuffer(GL_ARRAY_BUFFER, ps->model->vertexBufferId); 463 | glActiveTexture(GL_TEXTURE0); 464 | glBindTexture(GL_TEXTURE_2D, ps->model->textureId); 465 | glVertexAttribPointer(positionAttributeLocation, 2, GL_FLOAT, 0, 16, 0); 466 | glVertexAttribPointer(texcoordAttributeLocation, 2, GL_FLOAT, 0, 16, 8); 467 | glUniform1i(samplerUniformLocation, 0); 468 | 469 | unsigned int numTriangles = ps->model->numVertices / 4; 470 | for(unsigned int i = 0; i < ps->alive; ++i) { 471 | Particle const* p = &ps->particles[i]; 472 | glUniform1f(opacityUniformLocation, p->life/(float)p->totalLife); 473 | glUniform4fv(offsetUniformLocation, p->position.x, p->position.y, 0.0f, 0.0f); 474 | glDrawArrays(GL_TRIANGLES, 0, numTriangles); 475 | } 476 | } 477 | -------------------------------------------------------------------------------- /pong/main.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bzar/wasm-intro/d53c653b35649f9433ef5e7f5fff9da49483221b/pong/main.wasm -------------------------------------------------------------------------------- /strings/Makefile: -------------------------------------------------------------------------------- 1 | include ../Makefile 2 | 3 | all: main.wasm 4 | 5 | serve: 6 | python3 -m http.server 7 | -------------------------------------------------------------------------------- /strings/README.md: -------------------------------------------------------------------------------- 1 | # wasm-intro/strings 2 | 3 | A trivial example of: 4 | 5 | - Defining a WASM function in C 6 | - Defining external functions callable from C 7 | - Accessing WASM linear memory from both JavaScript and C 8 | - Using unicode, pointers and lengths to perform opaque string manipulation in C 9 | - Loading a WASM module and calling a WASM function from JavaScript and vice versa 10 | 11 | The page loads the WASM module, then manipulates and reads its exported memory using both JavaScript typed arrays and C pointers to transform a logged message into a palindrome. 12 | 13 | 14 | -------------------------------------------------------------------------------- /strings/index.html: -------------------------------------------------------------------------------- 1 | 43 | -------------------------------------------------------------------------------- /strings/main.c: -------------------------------------------------------------------------------- 1 | extern int consoleLog(unsigned int, unsigned int len); 2 | 3 | unsigned int* memory = 0; 4 | 5 | void mirrorLog(unsigned int offset, unsigned int len) { 6 | unsigned int* s = memory + offset; 7 | unsigned int responseOffset = 128; // arbitrary number, we have ALL the memory 8 | unsigned int* response = memory + responseOffset; 9 | for(unsigned int i = 0; i < len; ++i) { 10 | response[i] = s[i]; 11 | response[len*2-i-1] = s[i]; 12 | } 13 | consoleLog(responseOffset, len*2); 14 | } 15 | -------------------------------------------------------------------------------- /strings/main.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bzar/wasm-intro/d53c653b35649f9433ef5e7f5fff9da49483221b/strings/main.wasm -------------------------------------------------------------------------------- /tools/Makefile: -------------------------------------------------------------------------------- 1 | ROOT_DIR=$(shell pwd) 2 | LLVM_WORKDIR=$(ROOT_DIR)/src/llvm 3 | LLVM_BUILDDIR=$(ROOT_DIR)/build/llvm 4 | BINARYEN_WORKDIR=$(ROOT_DIR)/src/binaryen 5 | BINARYEN_BUILDDIR=$(ROOT_DIR)/build/binaryen 6 | WABT_WORKDIR=$(ROOT_DIR)/src/wabt 7 | WABT_BUILDDIR=$(ROOT_DIR)/build/wabt 8 | INSTALLDIR=$(ROOT_DIR)/bin 9 | 10 | build: build-llvm build-binaryen build-wabt 11 | 12 | clean: clean-llvm clean-binaryen clean-wabt 13 | rm -rf $(INSTALLDIR) 14 | 15 | install: install-llvm install-binaryen install-wabt 16 | 17 | $(LLVM_WORKDIR)/.svn: 18 | rm -rf $(LLVM_WORKDIR) 19 | mkdir -p $(LLVM_WORKDIR) 20 | svn co http://llvm.org/svn/llvm-project/llvm/trunk $(LLVM_WORKDIR) 21 | svn co http://llvm.org/svn/llvm-project/cfe/trunk $(LLVM_WORKDIR)/tools/clang 22 | svn co http://llvm.org/svn/llvm-project/compiler-rt/trunk $(LLVM_WORKDIR)/projects/compiler-rt 23 | 24 | $(LLVM_BUILDDIR)/Makefile: $(LLVM_WORKDIR)/.svn 25 | mkdir -p $(LLVM_BUILDDIR) 26 | cd $(LLVM_BUILDDIR); cmake -G "Unix Makefiles" -DCMAKE_INSTALL_PREFIX=$(INSTALLDIR) -DLLVM_EXPERIMENTAL_TARGETS_TO_BUILD=WebAssembly -DLLVM_TARGETS_TO_BUILD= -DCMAKE_BUILD_TYPE=Release $(LLVM_WORKDIR) 27 | 28 | build-llvm: $(LLVM_BUILDDIR)/Makefile 29 | $(MAKE) -C $(LLVM_BUILDDIR) -j 8 clang llc llvm-lib 30 | 31 | clean-llvm: 32 | rm -rf $(LLVM_WORKDIR) 33 | rm -rf $(LLVM_BUILDDIR) 34 | 35 | install-llvm: 36 | mkdir -p $(INSTALLDIR) 37 | $(MAKE) -C $(LLVM_BUILDDIR) install-clang install-llc install-llvm-lib 38 | 39 | $(BINARYEN_WORKDIR)/.git: 40 | rm -rf $(BINARYEN_WORKDIR) 41 | mkdir -p $(BINARYEN_WORKDIR) 42 | git clone https://github.com/WebAssembly/binaryen $(BINARYEN_WORKDIR) 43 | 44 | $(BINARYEN_BUILDDIR)/Makefile: $(BINARYEN_WORKDIR)/.git 45 | mkdir -p $(BINARYEN_BUILDDIR) 46 | cd $(BINARYEN_BUILDDIR); cmake -G "Unix Makefiles" -DCMAKE_INSTALL_PREFIX=$(INSTALLDIR) -DCMAKE_BUILD_TYPE=Release $(BINARYEN_WORKDIR) 47 | 48 | build-binaryen: $(BINARYEN_BUILDDIR)/Makefile 49 | $(MAKE) -C $(BINARYEN_BUILDDIR) -j 8 wasm-as wasm-dis s2wasm 50 | 51 | clean-binaryen: 52 | rm -rf $(BINARYEN_WORKDIR) 53 | rm -rf $(BINARYEN_BUILDDIR) 54 | 55 | install-binaryen: 56 | mkdir -p $(INSTALLDIR) 57 | cp $(addprefix $(BINARYEN_BUILDDIR)/bin/, wasm-as wasm-dis s2wasm) $(INSTALLDIR) 58 | 59 | $(WABT_WORKDIR)/.git: 60 | rm -rf $(WABT_WORKDIR) 61 | mkdir -p $(WABT_WORKDIR) 62 | git clone --recursive https://github.com/WebAssembly/wabt.git $(WABT_WORKDIR) 63 | 64 | $(WABT_BUILDDIR)/Makefile: $(WABT_WORKDIR)/.git 65 | mkdir -p $(WABT_BUILDDIR) 66 | cd $(WABT_BUILDDIR); cmake -G "Unix Makefiles" -DCMAKE_INSTALL_PREFIX=$(INSTALLDIR) -DCMAKE_BUILD_TYPE=Release $(WABT_WORKDIR) 67 | 68 | build-wabt: $(WABT_BUILDDIR)/Makefile 69 | $(MAKE) -C $(WABT_BUILDDIR) -j 8 70 | 71 | clean-wabt: 72 | rm -rf $(WABT_WORKDIR) 73 | rm -rf $(WABT_BUILDDIR) 74 | 75 | install-wabt: 76 | mkdir -p $(INSTALLDIR) 77 | $(MAKE) -C $(WABT_BUILDDIR) -j 8 install 78 | 79 | .PHONY: build clean install build-llvm clean-llvm install-llvm build-binaryen clean-binaryen install-binaryen build-wabt clean-wabt install-wabt 80 | 81 | -------------------------------------------------------------------------------- /wat/Makefile: -------------------------------------------------------------------------------- 1 | include ../Makefile 2 | INPUTS = $(wildcard *.wat) 3 | OUTPUTS = $(patsubst %.wat,%.wasm,$(INPUTS)) 4 | 5 | all: $(OUTPUTS) 6 | 7 | serve: 8 | python3 -m http.server 9 | -------------------------------------------------------------------------------- /wat/add.wasm: -------------------------------------------------------------------------------- 1 | asm```envprintIntmain 2 |  j 3 | A A -------------------------------------------------------------------------------- /wat/add.wat: -------------------------------------------------------------------------------- 1 | (module 2 | (import "env" "printInt" (func $printInt (param i32))) 3 | (func $add (param $lhs i32) (param $rhs i32) (result i32) 4 | get_local $lhs 5 | get_local $rhs 6 | i32.add 7 | ) 8 | 9 | (func $main 10 | (call $printInt 11 | (call $add (i32.const 9) (i32.const 8)))) 12 | 13 | (export "main" (func $main)) 14 | ) 15 | -------------------------------------------------------------------------------- /wat/fibonacci.wasm: -------------------------------------------------------------------------------- 1 | asm````envprintIntenvprintmainmemory 2 | |AA AA # E@   j!  Ak -@ E@   j!  k! Ak!  AAA AAA  A  3 | A  -------------------------------------------------------------------------------- /wat/fibonacci.wat: -------------------------------------------------------------------------------- 1 | (module 2 | (import "env" "printInt" (func $printInt (param i32))) 3 | (import "env" "print" (func $print (param i32 i32))) 4 | 5 | (memory $memory 1) 6 | (data (i32.const 0) "\n") 7 | (data (i32.const 1) " ") 8 | 9 | (func $endl 10 | (call $print (i32.const 0) (i32.const 1))) 11 | (func $space 12 | (call $print (i32.const 1) (i32.const 1))) 13 | 14 | (func $fibonacci_rec (param $a i32) (param $b i32) (param $n i32) (result i32) 15 | (if (i32.eqz (get_local $n)) (return (get_local $a))) 16 | (call $printInt (get_local $b)) 17 | (call $space) 18 | (set_local $a (i32.add (get_local $a) (get_local $b))) 19 | (call $fibonacci_rec (get_local $b) (get_local $a) (i32.sub (get_local $n) (i32.const 1))) 20 | ) 21 | 22 | (func $fibonacci_iter (param $a i32) (param $b i32) (param $n i32) (result i32) 23 | (loop $fi 24 | (if (i32.eqz (get_local $n)) (return (get_local $a))) 25 | (call $printInt (get_local $b)) 26 | (call $space) 27 | (set_local $b (i32.add (get_local $a) (get_local $b))) 28 | (set_local $a (i32.sub (get_local $b) (get_local $a))) 29 | (set_local $n (i32.sub (get_local $n) (i32.const 1))) 30 | (br $fi)) 31 | (get_local $b)) 32 | 33 | (func $main 34 | (drop (call $fibonacci_rec (i32.const 0) (i32.const 1) (i32.const 9))) 35 | (call $endl) 36 | (drop (call $fibonacci_iter (i32.const 0) (i32.const 1) (i32.const 9)))) 37 | 38 | (export "main" (func $main)) 39 | (export "memory" (memory $memory)) 40 | ) 41 | 42 | -------------------------------------------------------------------------------- /wat/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
9 | 10 | 11 |
12 |

13 | 
14 |     
46 |   
47 | 
48 | 


--------------------------------------------------------------------------------
/wat/print.wasm:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bzar/wasm-intro/d53c653b35649f9433ef5e7f5fff9da49483221b/wat/print.wasm


--------------------------------------------------------------------------------
/wat/print.wat:
--------------------------------------------------------------------------------
 1 | (module
 2 |   (import "env" "printInt" (func $printInt (param i32)))
 3 |   (import "env" "printFloat" (func $printFloat (param f32)))
 4 |   (import "env" "print" (func $print (param i32 i32)))
 5 | 
 6 |   (memory $memory 1)
 7 |   (data (i32.const 0) "\n")
 8 |   (data (i32.const 1) "Hello World!")
 9 | 
10 |   (func $endl
11 |     (call $print (i32.const 0) (i32.const 1)))
12 | 
13 |   (func $main
14 |     (call $printInt (i32.const 9))
15 |     (call $endl)
16 |     (call $printFloat (f32.const 6.28))
17 |     (call $endl)
18 |     (call $print (i32.const 1) (i32.const 12))
19 |     )
20 | 
21 |   (export "main" (func $main))
22 |   (export "memory" (memory $memory))
23 | )
24 | 


--------------------------------------------------------------------------------
/webgl/Makefile:
--------------------------------------------------------------------------------
1 | include ../Makefile
2 | 
3 | all: main.wasm
4 | 
5 | serve:
6 | 	python3 -m http.server
7 | 


--------------------------------------------------------------------------------
/webgl/README.md:
--------------------------------------------------------------------------------
 1 | # wasm-intro/webgl
 2 | 
 3 | A trivial example of:
 4 | 
 5 | - Defining a WASM function in C
 6 | - Wrapping WebGL API for calling from WASM
 7 | - Animation through onAnimationFrame -> WASM -> Canvas API
 8 | - Loading a WASM module, providing external functions and calling a WASM function from JavaScript and vice versa
 9 | 
10 | The page loads the WASM module and calls it each animation frame to draw a moving triangle on a canvas using WebGL.
11 | 
12 | 


--------------------------------------------------------------------------------
/webgl/index.html:
--------------------------------------------------------------------------------
  1 | 
  2 | 
  3 | 
  4 |   
  5 |   
  6 | 
  7 | 
  8 |   
9 | 10 |
11 | 12 | 105 | 106 | 107 | -------------------------------------------------------------------------------- /webgl/main.c: -------------------------------------------------------------------------------- 1 | extern unsigned int compileShader(char const* source, unsigned int len, unsigned int type); 2 | extern unsigned int linkShaderProgram(unsigned int vertexShaderId, unsigned int fragmentShaderId); 3 | 4 | extern void glClearColor(float, float, float, float); 5 | extern void glEnable(unsigned int); 6 | extern void glDepthFunc(unsigned int); 7 | extern void glClear(unsigned int); 8 | extern int glGetAttribLocation(unsigned int, char const*, unsigned int); 9 | extern int glGetUniformLocation(unsigned int, char const*, unsigned int); 10 | extern void glUniform4fv(int, float, float, float, float); 11 | extern unsigned int glCreateBuffer(); 12 | extern void glBindBuffer(unsigned int, unsigned int); 13 | extern void glBufferData(unsigned int, float*, unsigned int, unsigned int); 14 | extern void glUseProgram(unsigned int); 15 | extern void glEnableVertexAttribArray(unsigned int); 16 | extern void glVertexAttribPointer(unsigned int, unsigned int, unsigned int, unsigned int, unsigned int, unsigned int); 17 | extern void glDrawArrays(unsigned int, unsigned int, unsigned int); 18 | 19 | // Identifier constants pulled from WebGLRenderingContext 20 | unsigned int GL_VERTEX_SHADER = 35633; 21 | unsigned int GL_FRAGMENT_SHADER = 35632; 22 | unsigned int GL_ARRAY_BUFFER = 34962; 23 | unsigned int GL_TRIANGLES = 4; 24 | unsigned int GL_STATIC_DRAW = 35044; 25 | unsigned int GL_FLOAT = 5126; 26 | unsigned int GL_DEPTH_TEST = 2929; 27 | unsigned int GL_LEQUAL = 515; 28 | unsigned int GL_COLOR_BUFFER_BIT = 16384; 29 | unsigned int GL_DEPTH_BUFFER_BIT = 256; 30 | 31 | char vertexShader[] = 32 | "attribute vec4 a_position;" 33 | "uniform vec4 u_offset;" 34 | "void main() {" 35 | " gl_Position = a_position + u_offset;" 36 | "}"; 37 | 38 | char fragmentShader[] = 39 | "precision mediump float;" 40 | "void main() {" 41 | " gl_FragColor = vec4(0.3, 0.6, 1.0, 1.0);" 42 | "}"; 43 | 44 | float positions[] = {0, 0, 0, 0.5, 0.7, 0}; 45 | 46 | unsigned int programId; 47 | int positionAttributeLocation; 48 | int offsetUniformLocation; 49 | unsigned int positionBuffer; 50 | 51 | void onInit() { 52 | glClearColor(0.1, 0.1, 0.1, 1.0); 53 | glEnable(GL_DEPTH_TEST); 54 | glDepthFunc(GL_LEQUAL); 55 | glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); 56 | 57 | unsigned int vsId = compileShader(vertexShader, sizeof(vertexShader) - 1, GL_VERTEX_SHADER); 58 | unsigned int fsId = compileShader(fragmentShader, sizeof(fragmentShader) - 1, GL_FRAGMENT_SHADER); 59 | 60 | programId = linkShaderProgram(vsId, fsId); 61 | 62 | positionAttributeLocation = glGetAttribLocation(programId, "a_position", sizeof("a_position") - 1); 63 | offsetUniformLocation = glGetUniformLocation(programId, "u_offset", sizeof("u_offset") - 1); 64 | 65 | positionBuffer = glCreateBuffer(); 66 | glBindBuffer(GL_ARRAY_BUFFER, positionBuffer); 67 | glBufferData(GL_ARRAY_BUFFER, positions, 6, GL_STATIC_DRAW); 68 | } 69 | 70 | void onAnimationFrame(int timestamp) { 71 | static int previous = 0; 72 | static float x = 0; 73 | 74 | int delta = previous ? timestamp - previous : 0; 75 | x += delta / 1000.0; 76 | if(x > 1) x = -2; 77 | 78 | glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); 79 | 80 | glUseProgram(programId); 81 | glEnableVertexAttribArray(positionAttributeLocation); 82 | glBindBuffer(GL_ARRAY_BUFFER, positionBuffer); 83 | glVertexAttribPointer(positionAttributeLocation, 2, GL_FLOAT, 0, 0, 0); 84 | glUniform4fv(offsetUniformLocation, x, 0.0f, 0.0f, 0.0f); 85 | glDrawArrays(GL_TRIANGLES, 0, 3); 86 | previous = timestamp; 87 | } 88 | -------------------------------------------------------------------------------- /webgl/main.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bzar/wasm-intro/d53c653b35649f9433ef5e7f5fff9da49483221b/webgl/main.wasm --------------------------------------------------------------------------------