├── .gitignore
├── .travis.yml
├── CNAME
├── LICENSE-CC0
├── Makefile
├── README.md
├── _cobalt.yml
├── _includes
├── _footer.liquid
├── _head.liquid
└── _menu.liquid
├── _layouts
├── default.liquid
├── post.liquid
└── site.liquid
├── assets
├── default.min.css
└── highlight.min.js
├── bootstrap.css
├── demos
├── add
│ ├── Makefile
│ ├── add.rs
│ ├── add.wasm
│ ├── add.wat
│ └── index.liquid
├── bundle.js
├── call-js
│ ├── Makefile
│ ├── call-js.rs
│ ├── call-js.wasm
│ ├── call-js.wat
│ └── index.liquid
├── canvas
│ ├── Cargo.lock
│ ├── Cargo.toml
│ ├── Makefile
│ ├── canvas.wasm
│ ├── index.liquid
│ ├── script.js
│ └── src
│ │ └── main.rs
├── factorial
│ ├── Makefile
│ ├── factorial.rs
│ ├── factorial.wasm
│ ├── factorial.wat
│ └── index.liquid
├── feistel
│ ├── Cargo.lock
│ ├── Cargo.toml
│ ├── Makefile
│ ├── feistel.rs
│ ├── feistel.wasm
│ ├── feistel.wat
│ ├── index.liquid
│ └── script.js
├── import-memory
│ ├── .gitignore
│ ├── Cargo.lock
│ ├── Cargo.toml
│ ├── Makefile
│ ├── index.liquid
│ ├── string-passing.rs
│ ├── string-passing.wasm
│ └── string-passing.wat
├── index.md
└── sha1
│ ├── .gitignore
│ ├── Cargo.lock
│ ├── Cargo.toml
│ ├── Makefile
│ ├── index.liquid
│ ├── sha1-digest.rs
│ ├── sha1-digest.wasm
│ └── sha1-digest.wat
├── index.liquid
├── info.md
├── news.liquid
├── news
├── 2017-12-03-canvas.md
├── 2017-12-04-brainfuck-interpreter.md
├── 2017-12-04-rocket-a-game-on-wasm.md
├── 2017-12-05-semver-with-rust-and-wasm.md
├── 2017-12-06-the-case-for-wasm.md
├── 2017-12-07-import-memory.md
├── 2017-12-14-chip8-emulator-on-wasm.md
├── 2018-01-08-new-years-roundup.md
├── 2018-01-09-turtles-on-wasm.md
├── 2018-01-19-wasm-news.md
├── 2019-01-22-deprecation.md
├── big-factorial.md
├── minimal-example.md
├── native-wasm-target.md
└── post-1.md
├── resources.md
├── robots.txt
├── setup
├── docker.md
├── emscripten.md
├── index.md
└── wasm-target.md
├── style.css
└── talks.md
/.gitignore:
--------------------------------------------------------------------------------
1 | build
2 | *.big.wasm
3 | target
4 | drafts
5 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | sudo: false
2 | language: rust
3 |
4 | before_script:
5 | - curl -LSfs https://japaric.github.io/trust/install.sh | sh -s -- --git cobalt-org/cobalt.rs --crate cobalt --force --target x86_64-unknown-linux-gnu
6 | - export PATH="$PATH:/home/travis/.cargo/bin"
7 |
8 | script:
9 | - cobalt build
10 |
11 | branches:
12 | only:
13 | - staging
14 | - trying
15 | - master
16 |
17 | deploy:
18 | provider: pages
19 | skip_cleanup: true
20 | github_token: $GH_TOKEN
21 | local_dir: build
22 | on:
23 | branch: master
24 |
25 | env:
26 | global:
27 | secure: CoyWcelKZrFd9rY+hTPaUU40VcrZ4BvC5yL9xdkci5B+v/mseNo73K16k9ONk9pDMmoqNGx0FqF/le+CBY1NSJtXAPRIJKuZix5VRLd4y3o+QYzySpEie/Afjvny3vaMTb8bb8WkMSwswt0QRTiqgj7Xc2SUFFlkGkv/hmLeMu2Rx8IBiWJy2mhLipaxrDd8tkuUPlOy+sgNNjChLZfh8PexYI6TJlt2uZef2wBp1iM2aQFgH5iH7Gf+CnG3cbEkr+1IdH312+0vCJQl1RdZ8noNVECa/2lYeTPdZlqCbw1jG9CJsaozVYaCx6sa5tdT/UwzqoGq6Go3BvzO3VGzSF454EHhOCSUK08jwE5yG5yxmhg/KfqM0zu6IrXFtrs/9B5toR8D2DtUkP7YvGpp47RByCLteESEpfddyZKMaZafnLEBH6nTXJAhbdKzXrvITB88kCnDL9eFyF+ikPJi0qtEriVg5h1XMNxnu5u0L7dRNcq7388nP0xklGuRYCsypUDCgT+9gOQfK/YfPFVn0SDiQngkCC5XFLKRuSyY5gQd9xAkQw6YRsYx2HSCjN1TWoPS50L/7muNhPeTESwhldzWckcQBvJnOrnPczbRmya5Lx5TG6QgwK7kNPILEdKMh5nmVTk4Db+FjMT2k6/6dWYiq/lNjTg8aPnIrV5DIr8=
28 |
29 | notifications:
30 | email: false
31 |
--------------------------------------------------------------------------------
/CNAME:
--------------------------------------------------------------------------------
1 | www.hellorust.com
2 |
--------------------------------------------------------------------------------
/LICENSE-CC0:
--------------------------------------------------------------------------------
1 | CC0 1.0 Universal
2 |
3 | Statement of Purpose
4 |
5 | The laws of most jurisdictions throughout the world automatically confer
6 | exclusive Copyright and Related Rights (defined below) upon the creator and
7 | subsequent owner(s) (each and all, an "owner") of an original work of
8 | authorship and/or a database (each, a "Work").
9 |
10 | Certain owners wish to permanently relinquish those rights to a Work for the
11 | purpose of contributing to a commons of creative, cultural and scientific
12 | works ("Commons") that the public can reliably and without fear of later
13 | claims of infringement build upon, modify, incorporate in other works, reuse
14 | and redistribute as freely as possible in any form whatsoever and for any
15 | purposes, including without limitation commercial purposes. These owners may
16 | contribute to the Commons to promote the ideal of a free culture and the
17 | further production of creative, cultural and scientific works, or to gain
18 | reputation or greater distribution for their Work in part through the use and
19 | efforts of others.
20 |
21 | For these and/or other purposes and motivations, and without any expectation
22 | of additional consideration or compensation, the person associating CC0 with a
23 | Work (the "Affirmer"), to the extent that he or she is an owner of Copyright
24 | and Related Rights in the Work, voluntarily elects to apply CC0 to the Work
25 | and publicly distribute the Work under its terms, with knowledge of his or her
26 | Copyright and Related Rights in the Work and the meaning and intended legal
27 | effect of CC0 on those rights.
28 |
29 | 1. Copyright and Related Rights. A Work made available under CC0 may be
30 | protected by copyright and related or neighboring rights ("Copyright and
31 | Related Rights"). Copyright and Related Rights include, but are not limited
32 | to, the following:
33 |
34 | i. the right to reproduce, adapt, distribute, perform, display, communicate,
35 | and translate a Work;
36 |
37 | ii. moral rights retained by the original author(s) and/or performer(s);
38 |
39 | iii. publicity and privacy rights pertaining to a person's image or likeness
40 | depicted in a Work;
41 |
42 | iv. rights protecting against unfair competition in regards to a Work,
43 | subject to the limitations in paragraph 4(a), below;
44 |
45 | v. rights protecting the extraction, dissemination, use and reuse of data in
46 | a Work;
47 |
48 | vi. database rights (such as those arising under Directive 96/9/EC of the
49 | European Parliament and of the Council of 11 March 1996 on the legal
50 | protection of databases, and under any national implementation thereof,
51 | including any amended or successor version of such directive); and
52 |
53 | vii. other similar, equivalent or corresponding rights throughout the world
54 | based on applicable law or treaty, and any national implementations thereof.
55 |
56 | 2. Waiver. To the greatest extent permitted by, but not in contravention of,
57 | applicable law, Affirmer hereby overtly, fully, permanently, irrevocably and
58 | unconditionally waives, abandons, and surrenders all of Affirmer's Copyright
59 | and Related Rights and associated claims and causes of action, whether now
60 | known or unknown (including existing as well as future claims and causes of
61 | action), in the Work (i) in all territories worldwide, (ii) for the maximum
62 | duration provided by applicable law or treaty (including future time
63 | extensions), (iii) in any current or future medium and for any number of
64 | copies, and (iv) for any purpose whatsoever, including without limitation
65 | commercial, advertising or promotional purposes (the "Waiver"). Affirmer makes
66 | the Waiver for the benefit of each member of the public at large and to the
67 | detriment of Affirmer's heirs and successors, fully intending that such Waiver
68 | shall not be subject to revocation, rescission, cancellation, termination, or
69 | any other legal or equitable action to disrupt the quiet enjoyment of the Work
70 | by the public as contemplated by Affirmer's express Statement of Purpose.
71 |
72 | 3. Public License Fallback. Should any part of the Waiver for any reason be
73 | judged legally invalid or ineffective under applicable law, then the Waiver
74 | shall be preserved to the maximum extent permitted taking into account
75 | Affirmer's express Statement of Purpose. In addition, to the extent the Waiver
76 | is so judged Affirmer hereby grants to each affected person a royalty-free,
77 | non transferable, non sublicensable, non exclusive, irrevocable and
78 | unconditional license to exercise Affirmer's Copyright and Related Rights in
79 | the Work (i) in all territories worldwide, (ii) for the maximum duration
80 | provided by applicable law or treaty (including future time extensions), (iii)
81 | in any current or future medium and for any number of copies, and (iv) for any
82 | purpose whatsoever, including without limitation commercial, advertising or
83 | promotional purposes (the "License"). The License shall be deemed effective as
84 | of the date CC0 was applied by Affirmer to the Work. Should any part of the
85 | License for any reason be judged legally invalid or ineffective under
86 | applicable law, such partial invalidity or ineffectiveness shall not
87 | invalidate the remainder of the License, and in such case Affirmer hereby
88 | affirms that he or she will not (i) exercise any of his or her remaining
89 | Copyright and Related Rights in the Work or (ii) assert any associated claims
90 | and causes of action with respect to the Work, in either case contrary to
91 | Affirmer's express Statement of Purpose.
92 |
93 | 4. Limitations and Disclaimers.
94 |
95 | a. No trademark or patent rights held by Affirmer are waived, abandoned,
96 | surrendered, licensed or otherwise affected by this document.
97 |
98 | b. Affirmer offers the Work as-is and makes no representations or warranties
99 | of any kind concerning the Work, express, implied, statutory or otherwise,
100 | including without limitation warranties of title, merchantability, fitness
101 | for a particular purpose, non infringement, or the absence of latent or
102 | other defects, accuracy, or the present or absence of errors, whether or not
103 | discoverable, all to the greatest extent permissible under applicable law.
104 |
105 | c. Affirmer disclaims responsibility for clearing rights of other persons
106 | that may apply to the Work or any use thereof, including without limitation
107 | any person's Copyright and Related Rights in the Work. Further, Affirmer
108 | disclaims responsibility for obtaining any necessary consents, permissions
109 | or other rights required for any use of the Work.
110 |
111 | d. Affirmer understands and acknowledges that Creative Commons is not a
112 | party to this document and has no duty or obligation with respect to this
113 | CC0 or use of the Work.
114 |
115 | For more information, please see
116 |
13 | The Rust Community team is dedicated to bringing new people into the 14 | Rust Programming Language's community, 15 | and helping established Rust communities thrive. 16 |
17 |8 | Click the button to run the WebAssembly code. 9 |
10 | 11 |12 | Based on 13 | an example 14 | by Steve Klabnik. 15 |
16 | 17 | 18 | 19 | 30 | 31 |
36 | Download add.rs
37 |
38 | Download add.wat (WebAssembly text format)
39 |
#[no_mangle]
42 | pub extern "C" fn add_one(x: i32) -> i32 {
43 | x + 1
44 | }
45 |
46 |
47 | 48 | Compile this code as follows: 49 |
50 | 51 |rustup update
52 | rustup target add wasm32-unknown-unknown --toolchain nightly
53 | rustc +nightly --target wasm32-unknown-unknown -O --crate-type=cdylib add.rs -o add.big.wasm
54 |
55 | Reduce the size by removing unused stuff from the module:
56 | 57 |cargo install --git https://github.com/alexcrichton/wasm-gc
58 | wasm-gc add.big.wasm add.wasm
59 |
60 | 61 | The JavaScript code loads the WebAssembly module and has access to the exported function. 62 |
63 | 64 |fetch('add.wasm')
65 | .then(response => response.arrayBuffer())
66 | .then(bytes => WebAssembly.instantiate(bytes, {}))
67 | .then(results => {
68 | alert(results.instance.exports.add_one(41));
69 | });
70 |
71 | 72 | The readable Webassembly looks like this: 73 |
74 | 75 |(module
76 | (type (;0;) (func (param i32) (result i32)))
77 | (type (;1;) (func))
78 | (func (;0;) (type 0) (param i32) (result i32)
79 | get_local 0
80 | i32.const 1
81 | i32.add)
82 | (func (;1;) (type 1))
83 | (table (;0;) 0 anyfunc)
84 | (memory (;0;) 17)
85 | (export "memory" (memory 0))
86 | (export "add_one" (func 0))
87 | (export "rust_eh_personality" (func 1))
88 | (data (i32.const 4) "\10\00\10\00"))
89 |
--------------------------------------------------------------------------------
/demos/bundle.js:
--------------------------------------------------------------------------------
1 | function fetchAndInstantiate(url, importObject) {
2 | return fetch(url).then(response =>
3 | response.arrayBuffer()
4 | ).then(bytes =>
5 | WebAssembly.instantiate(bytes, importObject)
6 | ).then(results =>
7 | results.instance
8 | );
9 | }
10 |
11 | // Copy a nul-terminated string from the buffer pointed to.
12 | // Consumes the old data and thus deallocated it.
13 | function copyCStr(module, ptr) {
14 | let orig_ptr = ptr;
15 | const collectCString = function* () {
16 | let memory = new Uint8Array(module.memory.buffer);
17 | while (memory[ptr] !== 0) {
18 | if (memory[ptr] === undefined) { throw new Error("Tried to read undef mem") }
19 | yield memory[ptr]
20 | ptr += 1
21 | }
22 | }
23 |
24 | const buffer_as_u8 = new Uint8Array(collectCString())
25 | const utf8Decoder = new TextDecoder("UTF-8");
26 | const buffer_as_utf8 = utf8Decoder.decode(buffer_as_u8);
27 | module.dealloc_str(orig_ptr);
28 | return buffer_as_utf8
29 | }
30 |
31 | function getStr(module, ptr, len) {
32 | const getData = function* (ptr, len) {
33 | let memory = new Uint8Array(module.memory.buffer);
34 | for (let index = 0; index < len; index++) {
35 | if (memory[ptr] === undefined) { throw new Error(`Tried to read undef mem at ${ptr}`) }
36 | yield memory[ptr + index]
37 | }
38 | }
39 |
40 | const buffer_as_u8 = new Uint8Array(getData(ptr/8, len/8));
41 | const utf8Decoder = new TextDecoder("UTF-8");
42 | const buffer_as_utf8 = utf8Decoder.decode(buffer_as_u8);
43 | return buffer_as_utf8;
44 | }
45 |
46 | function newString(module, str) {
47 | const utf8Encoder = new TextEncoder("UTF-8");
48 | let string_buffer = utf8Encoder.encode(str)
49 | let len = string_buffer.length
50 | let ptr = module.alloc(len+1)
51 |
52 | let memory = new Uint8Array(module.memory.buffer);
53 | for (i = 0; i < len; i++) {
54 | memory[ptr+i] = string_buffer[i]
55 | }
56 |
57 | memory[ptr+len] = 0;
58 |
59 | return ptr;
60 | }
61 |
--------------------------------------------------------------------------------
/demos/call-js/Makefile:
--------------------------------------------------------------------------------
1 | NAME=call-js
2 |
3 | build: $(NAME).wasm $(NAME).wat
4 |
5 | %.wat: %.wasm
6 | wasm2wat $< > $@
7 |
8 | %.wasm: %.big.wasm
9 | wasm-gc $< $@
10 |
11 | %.big.wasm: %.rs
12 | rustc +nightly --target wasm32-unknown-unknown -O --crate-type=cdylib $< -o $@
13 |
--------------------------------------------------------------------------------
/demos/call-js/call-js.rs:
--------------------------------------------------------------------------------
1 | extern {
2 | fn alert(ptr: *const u8, number: u32);
3 | }
4 |
5 | #[no_mangle]
6 | pub extern "C" fn run() {
7 | unsafe {
8 | let x = b"Hello World!\0";
9 | alert(x as *const u8, 42);
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/demos/call-js/call-js.wasm:
--------------------------------------------------------------------------------
1 | asm ` `
envalert p &memory run rust_eh_personality
2 |
AA* A A
Hello World!
--------------------------------------------------------------------------------
/demos/call-js/call-js.wat:
--------------------------------------------------------------------------------
1 | (module
2 | (type (;0;) (func (param i32 i32)))
3 | (type (;1;) (func))
4 | (import "env" "alert" (func (;0;) (type 0)))
5 | (func (;1;) (type 1)
6 | i32.const 12
7 | i32.const 42
8 | call 0)
9 | (func (;2;) (type 1))
10 | (table (;0;) 0 anyfunc)
11 | (memory (;0;) 17)
12 | (export "memory" (memory 0))
13 | (export "run" (func 1))
14 | (export "rust_eh_personality" (func 2))
15 | (data (i32.const 4) " \00\10\00")
16 | (data (i32.const 12) "Hello World!\00"))
17 |
--------------------------------------------------------------------------------
/demos/call-js/index.liquid:
--------------------------------------------------------------------------------
1 | permalink: "/demos/call-js"
2 | title: "Demo: Call JavaScript from Rust"
3 | layout: site.liquid
4 | ---
5 | 8 | Click the button to run the WebAssembly code. 9 |
10 | 11 | 12 | 13 | 14 | 36 | 37 |
42 | Download call-js.rs
43 |
44 | Download call-js.wat (WebAssembly text format)
45 |
extern {
48 | fn alert(ptr: *const u8, number: u32);
49 | }
50 |
51 | #[no_mangle]
52 | pub extern "C" fn run() {
53 | unsafe {
54 | let x = b"Hello World!\0";
55 | alert(x as *const u8, 42);
56 | }
57 | }
58 |
59 | 60 | The JavaScript code loads the WebAssembly module and has access to the exported function. 61 |
62 | 63 |window.Module = {};
64 |
65 | const imports = {
66 | env: {
67 | alert: function(ptr, number) {
68 | let str = copyCStr(Module, ptr);
69 | alert(str + " -> " + number);
70 | }
71 | }
72 | };
73 |
74 | fetchAndInstantiate("./call-js.wasm", imports)
75 | .then(mod => {
76 | Module.memory = mod.exports.memory;
77 | Module.run = mod.exports.run;
78 | Module.dealloc_str = function() {}
79 |
80 | Module.run();
81 | });
82 |
83 |
--------------------------------------------------------------------------------
/demos/canvas/Cargo.lock:
--------------------------------------------------------------------------------
1 | [[package]]
2 | name = "rust-wasm-canvas"
3 | version = "0.1.0"
4 |
5 |
--------------------------------------------------------------------------------
/demos/canvas/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "rust-wasm-canvas"
3 | version = "0.1.0"
4 |
5 | [dependencies]
6 |
7 | [profile.release]
8 | opt-level = "s"
9 |
--------------------------------------------------------------------------------
/demos/canvas/Makefile:
--------------------------------------------------------------------------------
1 | all:
2 | cargo +nightly build --target=wasm32-unknown-unknown --release
3 | wasm-gc ./target/wasm32-unknown-unknown/release/rust-wasm-canvas.wasm ./canvas.wasm
4 |
--------------------------------------------------------------------------------
/demos/canvas/canvas.wasm:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/badboy/hellorust/f2dce7d25cd0a07b12a82c6738f205b1e67d8ee7/demos/canvas/canvas.wasm
--------------------------------------------------------------------------------
/demos/canvas/index.liquid:
--------------------------------------------------------------------------------
1 | permalink: /demos/canvas
2 | title: "Demo: Update a canvas from wasm"
3 | layout: site.liquid
4 | ---
5 | 8 | by Geoffroy Couprie 9 |
10 | 11 | 12 |14 | Caution: This will display a flickering image. 15 |
16 | 17 | 18 | 19 | 20 |25 | This example shares a buffer between the Javascript side and the Rust side, and uses 26 | it to update the raw pixels on a canvas. 27 |
28 |29 | Download main.rs 30 |
31 | 32 |use std::mem;
33 | use std::slice;
34 | use std::os::raw::c_void;
35 |
36 | // We need to provide an (empty) main function,
37 | // as the target currently is compiled as a binary.
38 | fn main() {}
39 |
40 | // In order to work with the memory we expose (de)allocation methods
41 | #[no_mangle]
42 | pub extern "C" fn alloc(size: usize) -> *mut c_void {
43 | let mut buf = Vec::with_capacity(size);
44 | let ptr = buf.as_mut_ptr();
45 | mem::forget(buf);
46 | return ptr as *mut c_void;
47 | }
48 |
49 | #[no_mangle]
50 | pub extern "C" fn dealloc(ptr: *mut c_void, cap: usize) {
51 | unsafe {
52 | let _buf = Vec::from_raw_parts(ptr, 0, cap);
53 | }
54 | }
55 |
56 | // the Javascript side passes a pointer to a buffer, the size of the corresponding canvas
57 | // and the current timestamp
58 | #[no_mangle]
59 | pub extern "C" fn fill(pointer: *mut u8, max_width: usize, max_height: usize, time: f64) {
60 |
61 | // pixels are stored in RGBA, so each pixel is 4 bytes
62 | let byte_size = max_width * max_height * 4;
63 | let sl = unsafe { slice::from_raw_parts_mut(pointer, byte_size) };
64 |
65 | for i in 0..byte_size {
66 |
67 | // get the position of current pixel
68 | let height = i / 4 / max_width;
69 | let width = i / 4 % max_width;
70 |
71 | if i%4 == 3 {
72 | // set opacity to 1
73 | sl[i] = 255;
74 | } else if i%4 == 0 {
75 | // create a red ripple effect from the top left corner
76 | let len = ((height*height + width*width) as f64).sqrt();
77 | let nb = time + len / 4.0;
78 | let a = 128.0 + nb.cos() * 128.0;
79 | sl[i] = a as u8;
80 | } else if i % 4 == 2 {
81 | // create a blue ripple effect from the top right corner
82 | let width = 500 - width;
83 | let len = ((height*height + width*width) as f64).sqrt();
84 | let nb = time + len / 4.0;
85 | let a = 128.0 + nb.cos() * 128.0;
86 | sl[i] = a as u8;
87 | }
88 | }
89 | }
90 |
91 |
92 |
93 | 94 | The JavaScript code loads the WebAssembly module and has access to the exported functions and fields, including the allocation and memory. 95 | We create an ImageData backed by memory allocated on the Rust side, and regularly update it. 96 |
97 | 98 |fetch("canvas.wasm").then(response =>
99 | response.arrayBuffer()
100 | ).then(bytes =>
101 |
102 | // the Rust side needs a cos function
103 | WebAssembly.instantiate(bytes, { env: { cos: Math.cos } })
104 | ).then(results => {
105 | let module = {};
106 | let mod = results.instance;
107 | module.alloc = mod.exports.alloc;
108 | module.dealloc = mod.exports.dealloc;
109 | module.fill = mod.exports.fill;
110 |
111 | var width = 500;
112 | var height = 500;
113 |
114 |
115 | var canvas = document.getElementById('screen');
116 | if (canvas.getContext) {
117 | var ctx = canvas.getContext('2d');
118 |
119 | let byteSize = width * height * 4;
120 | var pointer = module.alloc( byteSize );
121 |
122 | var usub = new Uint8ClampedArray(mod.exports.memory.buffer, pointer, byteSize);
123 | var img = new ImageData(usub, width, height);
124 |
125 | var start = null;
126 | function step(timestamp) {
127 | var progress;
128 | if (start === null) start = timestamp;
129 | progress = timestamp - start;
130 | if (progress > 100) {
131 | module.fill(pointer, width, height, timestamp);
132 |
133 | start = timestamp
134 |
135 | window.requestAnimationFrame(draw);
136 | } else {
137 | window.requestAnimationFrame(step);
138 | }
139 | }
140 |
141 | function draw() {
142 | ctx.putImageData(img, 0, 0)
143 | window.requestAnimationFrame(step);
144 | }
145 |
146 | window.requestAnimationFrame(step);
147 | }
148 |
149 | });
150 |
151 |
--------------------------------------------------------------------------------
/demos/canvas/script.js:
--------------------------------------------------------------------------------
1 | fetch("canvas.wasm").then(response =>
2 | response.arrayBuffer()
3 | ).then(bytes =>
4 | WebAssembly.instantiate(bytes, { env: { cos: Math.cos } })
5 | ).then(results => {
6 | let module = {};
7 | let mod = results.instance;
8 | module.alloc = mod.exports.alloc;
9 | module.dealloc = mod.exports.dealloc;
10 | module.fill = mod.exports.fill;
11 |
12 | var width = 500;
13 | var height = 500;
14 |
15 | let byteSize = width * height * 4;
16 | var pointer = module.alloc( byteSize );
17 | var buffer = new Uint8Array(mod.exports.memory.buffer, pointer, byteSize);
18 |
19 | var canvas = document.getElementById('screen');
20 | if (canvas.getContext) {
21 | var ctx = canvas.getContext('2d');
22 |
23 | var pointer = module.alloc( byteSize );
24 |
25 | var usub = new Uint8ClampedArray(mod.exports.memory.buffer, pointer, byteSize);
26 | var img = new ImageData(usub, width, height);
27 | var running = false;
28 |
29 | var start = null;
30 | function step(timestamp) {
31 | var progress;
32 | if (start === null) start = timestamp;
33 | progress = timestamp - start;
34 | if (progress > 100) {
35 | module.fill(pointer, width, height, timestamp);
36 |
37 | start = timestamp
38 |
39 | if (running)
40 | window.requestAnimationFrame(draw);
41 | } else {
42 | if (running)
43 | window.requestAnimationFrame(step);
44 | }
45 | }
46 |
47 | function draw() {
48 | ctx.putImageData(img, 0, 0)
49 | window.requestAnimationFrame(step);
50 | }
51 |
52 | //if (running)
53 | //window.requestAnimationFrame(step);
54 | var button = document.getElementById("run-wasm");
55 | button.addEventListener("click", function(e) {
56 | running = !running;
57 | if (running) {
58 | button.innerText = "Stop motion";
59 | window.requestAnimationFrame(step);
60 | } else {
61 | button.innerText = "Start motion";
62 | }
63 | });
64 | }
65 | });
66 |
67 |
--------------------------------------------------------------------------------
/demos/canvas/src/main.rs:
--------------------------------------------------------------------------------
1 | use std::mem;
2 | use std::slice;
3 | use std::os::raw::c_void;
4 |
5 | fn main() {}
6 |
7 | #[no_mangle]
8 | pub extern "C" fn alloc(size: usize) -> *mut c_void {
9 | let mut buf = Vec::with_capacity(size);
10 | let ptr = buf.as_mut_ptr();
11 | mem::forget(buf);
12 | return ptr as *mut c_void;
13 | }
14 |
15 | #[no_mangle]
16 | pub extern "C" fn dealloc(ptr: *mut c_void, cap: usize) {
17 | unsafe {
18 | let _buf = Vec::from_raw_parts(ptr, 0, cap);
19 | }
20 | }
21 |
22 | #[no_mangle]
23 | pub extern "C" fn fill(pointer: *mut u8, max_width: usize, max_height: usize, time: f64) {
24 |
25 | // pixels are stored in RGBA, so each pixel is 4 bytes
26 | let byte_size = max_width * max_height * 4;
27 | let sl = unsafe { slice::from_raw_parts_mut(pointer, byte_size) };
28 |
29 | for i in 0..byte_size {
30 |
31 | // get the position of current pixel
32 | let height = i / 4 / max_width;
33 | let width = i / 4 % max_width;
34 |
35 | if i%4 == 3 {
36 |
37 | // set opacity to 1
38 | sl[i] = 255;
39 |
40 | } else if i%4 == 0 {
41 |
42 | // create a red ripple effect from the top left corner
43 | let len = ((height*height + width*width) as f64).sqrt();
44 | let nb = time + len / 4.0;
45 | let a = 128.0 + nb.cos() * 128.0;
46 | sl[i] = a as u8;
47 |
48 |
49 | } else if i % 4 == 2 {
50 |
51 | // create a blue ripple effect from the top right corner
52 | let width = 500 - width;
53 | let len = ((height*height + width*width) as f64).sqrt();
54 | let nb = time + len / 4.0;
55 | let a = 128.0 + nb.cos() * 128.0;
56 | sl[i] = a as u8;
57 | }
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/demos/factorial/Makefile:
--------------------------------------------------------------------------------
1 | NAME=factorial
2 |
3 | build: $(NAME).wasm $(NAME).wat
4 |
5 | %.wat: %.wasm
6 | wasm2wat $< > $@
7 |
8 | %.wasm: %.big.wasm
9 | wasm-gc $< $@
10 |
11 | %.big.wasm: %.rs
12 | rustc +nightly --target wasm32-unknown-unknown -O --crate-type=cdylib $< -o $@
13 |
--------------------------------------------------------------------------------
/demos/factorial/factorial.rs:
--------------------------------------------------------------------------------
1 | use std::mem;
2 | use std::ffi::CString;
3 | use std::os::raw::{c_char, c_void};
4 |
5 | #[no_mangle]
6 | pub extern "C" fn alloc(size: usize) -> *mut c_void {
7 | let mut buf = Vec::with_capacity(size);
8 | let ptr = buf.as_mut_ptr();
9 | mem::forget(buf);
10 | return ptr as *mut c_void;
11 | }
12 |
13 | #[no_mangle]
14 | pub extern "C" fn dealloc_str(ptr: *mut c_char) {
15 | unsafe {
16 | let _ = CString::from_raw(ptr);
17 | }
18 | }
19 |
20 | #[no_mangle]
21 | pub extern "C" fn fact(n: u32) -> u64 {
22 | let mut n = n as u64;
23 | let mut result = 1;
24 | while n > 0 {
25 | result = result * n;
26 | n = n - 1;
27 | }
28 | result
29 | }
30 |
31 | #[no_mangle]
32 | pub extern "C" fn fact_str(n: u32) -> *mut c_char {
33 | let res = fact(n);
34 | let s = format!("{}", res);
35 | let s = CString::new(s).unwrap();
36 | s.into_raw()
37 | }
38 |
--------------------------------------------------------------------------------
/demos/factorial/factorial.wasm:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/badboy/hellorust/f2dce7d25cd0a07b12a82c6738f205b1e67d8ee7/demos/factorial/factorial.wasm
--------------------------------------------------------------------------------
/demos/factorial/index.liquid:
--------------------------------------------------------------------------------
1 | permalink: /demos/factorial
2 | title: "Demo: Factorial in WebAssembly"
3 | layout: site.liquid
4 | ---
5 | 8 | Type a number into the field and the factorial will be calculated. 9 | JavaScript has no plain integers, all numbers are actually 64-bit floats. 10 | It thus cannot correctly handle numbers bigger than 53 bits. 11 | However, in order to handle the factorial of up to 20, we need 64-bit integers. 12 | By doing the formatting in Rust (and thus WebAssembly) and returning a string, 13 | we can correctly print the result. 14 | For values larger than 20 the result is still incorrect. 15 |
16 | 17 |
64 | The Rust code implements a simple fact
function
65 | as well as a function formatting the 64-bit integer to a string called fact_str
.
66 |
68 | Download factorial.rs
69 |
70 | Download factorial.wat (WebAssembly text format)
71 |
fn main() {}
74 |
75 | #[no_mangle]
76 | pub extern "C" fn fact(mut n: u32) -> u64 {
77 | let n = n as u64;
78 | let mut result = 1;
79 | while n > 0 {
80 | result = result * n;
81 | n = n - 1;
82 | }
83 | result
84 | }
85 |
86 | #[no_mangle]
87 | pub extern "C" fn fact_str(n: u32) -> *mut c_char {
88 | let res = fact(n);
89 | let s = format!("{}", res);
90 | let s = CString::new(s).unwrap();
91 | s.into_raw()
92 | }
93 |
94 |
95 |
96 | The JavaScript code loads the WebAssembly module and has access to the exported function.
97 | However it can't call the fact
function,
98 | as it 64-bit integers can't be passed back to JavaScript.
99 | We therefore have to call the formatting function instead and extract the string.
100 | The helper function copyCStr
is defined in
101 | bundle.js
.
102 |
window.Module = {};
105 | fetchAndInstantiate("./factorial.wasm", {})
106 | .then(mod => {
107 | Module.fact = mod.exports.fact;
108 | Module.alloc = mod.exports.alloc;
109 | Module.dealloc_str = mod.exports.dealloc_str;
110 | Module.memory = mod.exports.memory;
111 | Module.fact_str = function(n) {
112 | let outptr = mod.exports.fact_str(n);
113 | let result = copyCStr(Module, outptr);
114 | return result;
115 | };
116 | })
117 |
118 |
119 | 120 | The compiled WebAssembly code is reasonably short. Try to understand what's happening! 121 |
122 | 123 |(func $fact (param i32) (result i64)
124 | (local i64 i64)
125 | block ;; label = @1
126 | block ;; label = @2
127 | get_local 0
128 | i32.eqz
129 | br_if 0 (;@2;)
130 | get_local 0
131 | i64.extend_u/i32
132 | set_local 1
133 | i64.const 1
134 | set_local 2
135 | loop ;; label = @3
136 | get_local 1
137 | get_local 2
138 | i64.mul
139 | set_local 2
140 | get_local 1
141 | i64.const -1
142 | i64.add
143 | tee_local 1
144 | i64.eqz
145 | i32.eqz
146 | br_if 0 (;@3;)
147 | br 2 (;@1;)
148 | end
149 | unreachable
150 | end
151 | i64.const 1
152 | set_local 2
153 | end
154 | get_local 2)
155 |
156 |
--------------------------------------------------------------------------------
/demos/feistel/Cargo.lock:
--------------------------------------------------------------------------------
1 | [[package]]
2 | name = "feistel"
3 | version = "0.1.0"
4 |
5 |
--------------------------------------------------------------------------------
/demos/feistel/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "feistel"
3 | version = "0.1.0"
4 |
5 | [lib]
6 | path = "feistel.rs"
7 | crate-type = ["cdylib"]
8 |
9 | [dependencies]
10 |
11 | [profile.release]
12 | opt-level = "s"
13 |
--------------------------------------------------------------------------------
/demos/feistel/Makefile:
--------------------------------------------------------------------------------
1 | NAME=feistel
2 |
3 | build: $(NAME).wasm $(NAME).wat
4 |
5 | %.wat: %.wasm
6 | wasm2wat $< > $@
7 |
8 | %.wasm: %.big.wasm
9 | wasm-gc $< $@
10 |
11 | %.big.wasm: %.rs
12 | cargo +nightly build --target wasm32-unknown-unknown --release
13 | cp ./target/wasm32-unknown-unknown/release/$(NAME).wasm $@
14 |
--------------------------------------------------------------------------------
/demos/feistel/feistel.rs:
--------------------------------------------------------------------------------
1 | use std::mem;
2 | use std::slice;
3 | use std::os::raw::c_void;
4 |
5 | #[no_mangle]
6 | pub extern "C" fn alloc(size: usize) -> *mut c_void {
7 | let mut buf = Vec::with_capacity(size);
8 | let ptr = buf.as_mut_ptr();
9 | mem::forget(buf);
10 | return ptr as *mut c_void;
11 | }
12 |
13 | #[no_mangle]
14 | pub extern "C" fn dealloc(ptr: *mut c_void, cap: usize) {
15 | unsafe {
16 | let _buf = Vec::from_raw_parts(ptr, 0, cap);
17 | }
18 | }
19 |
20 | fn feistel_net(input: u16) -> u16 {
21 | let mut l = input & 0xff;
22 | let mut r = input >> 8;
23 |
24 | for _ in 0..8 {
25 | let nl = r;
26 | let f = (((r * 11) + (r >> 5) + 7 * 127) ^ r) & 0xff;
27 | r = l ^ f;
28 | l = nl;
29 | }
30 |
31 | ((r<<8)|l) & 0xffff
32 | }
33 |
34 | #[test]
35 | fn feistel_fills_all() {
36 | let mut v = vec![0u8; 65536];
37 | let ones = vec![1u8; 65536];
38 |
39 | for frame in 0..65536u32 {
40 | let point = feistel_net(frame as u16);
41 | v[point as usize] = 1;
42 | }
43 |
44 | assert_eq!(&ones[0..10], &v[0..10]);
45 | }
46 |
47 | fn set_pixel(pixels: &mut [u8], width: u16, x: u16, y: u16) {
48 | let x = x as usize;
49 | let y = y as usize;
50 | let width = width as usize;
51 | let offset = x*4 + y*4 * width;
52 |
53 | pixels[(offset+0)] = 255;
54 | pixels[(offset+1)] = 0;
55 | pixels[(offset+2)] = 0;
56 | pixels[(offset+3)] = 255;
57 | }
58 |
59 | #[no_mangle]
60 | pub extern "C" fn clear(pointer: *mut u8, width: usize, height: usize) {
61 | let byte_size = width * height * 4;
62 | let buf = unsafe { slice::from_raw_parts_mut(pointer, byte_size) };
63 |
64 | for i in buf.iter_mut() {
65 | *i = 0;
66 | }
67 | }
68 |
69 | #[no_mangle]
70 | pub extern "C" fn fill(pointer: *mut u8, width: usize, height: usize, mut frame: u32) -> u32 {
71 | if frame == 65536 {
72 | return frame;
73 | }
74 |
75 | // pixels are stored in RGBA, so each pixel is 4 bytes
76 | let byte_size = width * height * 4;
77 | let buf = unsafe { slice::from_raw_parts_mut(pointer, byte_size) };
78 |
79 | let width = width as u16;
80 | let height = height as u16;
81 |
82 | for _ in 0..200 {
83 | if frame == 65536 {
84 | break;
85 | }
86 |
87 | let pos = feistel_net(frame as u16);
88 | let x = pos % width;
89 | let y = pos / width;
90 | if x < width && y < height {
91 | set_pixel(buf, width, x, y);
92 | }
93 |
94 | frame += 1;
95 | }
96 |
97 | frame
98 | }
99 |
--------------------------------------------------------------------------------
/demos/feistel/feistel.wasm:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/badboy/hellorust/f2dce7d25cd0a07b12a82c6738f205b1e67d8ee7/demos/feistel/feistel.wasm
--------------------------------------------------------------------------------
/demos/feistel/index.liquid:
--------------------------------------------------------------------------------
1 | permalink: /demos/feistel
2 | title: "Demo: FizzleFade effect using a Feistel network"
3 | layout: site.liquid
4 | ---
5 | 8 | based on code by Salvatore Sanfilippo (antirez). 9 |
10 | 11 | 12 |14 | Caution: This will display a flickering image. 15 |
16 | 17 | 18 | 19 | 20 |25 | This example shares a buffer between the Javascript side and the Rust side, 26 | and uses it to update the raw pixels on a canvas. 27 |
28 |
29 | Download feistel.rs
30 |
31 | Download feistel.wat (WebAssembly text format)
32 |
use std::mem;
35 | use std::slice;
36 | use std::os::raw::c_void;
37 |
38 | #[no_mangle]
39 | pub extern "C" fn alloc(size: usize) -> *mut c_void {
40 | let mut buf = Vec::with_capacity(size);
41 | let ptr = buf.as_mut_ptr();
42 | mem::forget(buf);
43 | return ptr as *mut c_void;
44 | }
45 |
46 | #[no_mangle]
47 | pub extern "C" fn dealloc(ptr: *mut c_void, cap: usize) {
48 | unsafe {
49 | let _buf = Vec::from_raw_parts(ptr, 0, cap);
50 | }
51 | }
52 |
53 | fn feistel_net(input: u16) -> u16 {
54 | let mut l = input & 0xff;
55 | let mut r = input >> 8;
56 |
57 | for _ in 0..8 {
58 | let nl = r;
59 | let f = (((r * 11) + (r >> 5) + 7 * 127) ^ r) & 0xff;
60 | r = l ^ f;
61 | l = nl;
62 | }
63 |
64 | ((r<<8)|l) & 0xffff
65 | }
66 |
67 | fn set_pixel(pixels: &mut [u8], width: u16, x: u16, y: u16) {
68 | let x = x as usize;
69 | let y = y as usize;
70 | let width = width as usize;
71 | let offset = x*4 + y*4 * width;
72 |
73 | pixels[(offset+0)] = 255;
74 | pixels[(offset+1)] = 0;
75 | pixels[(offset+2)] = 0;
76 | pixels[(offset+3)] = 255;
77 | }
78 |
79 | #[no_mangle]
80 | pub extern "C" fn clear(pointer: *mut u8, width: usize, height: usize) {
81 | let byte_size = width * height * 4;
82 | let buf = unsafe { slice::from_raw_parts_mut(pointer, byte_size) };
83 |
84 | for i in buf.iter_mut() {
85 | *i = 0;
86 | }
87 | }
88 |
89 | #[no_mangle]
90 | pub extern "C" fn fill(pointer: *mut u8, width: usize, height: usize, mut frame: u32) -> u32 {
91 | if frame == 65536 {
92 | return frame;
93 | }
94 |
95 | // pixels are stored in RGBA, so each pixel is 4 bytes
96 | let byte_size = width * height * 4;
97 | let buf = unsafe { slice::from_raw_parts_mut(pointer, byte_size) };
98 |
99 | let width = width as u16;
100 | let height = height as u16;
101 |
102 | for _ in 0..200 {
103 | if frame == 65536 {
104 | break;
105 | }
106 |
107 | let pos = feistel_net(frame as u16);
108 | let x = pos % width;
109 | let y = pos / width;
110 | if x < width && y < height {
111 | set_pixel(buf, width, x, y);
112 | }
113 |
114 | frame += 1;
115 | }
116 |
117 | frame
118 | }
119 |
120 |
121 |
122 | 123 | The JavaScript code loads the WebAssembly module and has access to the exported functions and fields, including the allocation and memory. 124 | We create an ImageData backed by memory allocated on the Rust side, and regularly update it. 125 |
126 | 127 |fetch("feistel.wasm", {cache: "no-cache"}).then(response =>
128 | response.arrayBuffer()
129 | ).then(bytes =>
130 | WebAssembly.instantiate(bytes, {})
131 | ).then(results => {
132 | let module = {};
133 | let mod = results.instance;
134 | module.alloc = mod.exports.alloc;
135 | module.dealloc = mod.exports.dealloc;
136 | module.fill = mod.exports.fill;
137 | module.clear = mod.exports.clear;
138 |
139 | var width = 320;
140 | var height = 200;
141 |
142 | let byteSize = width * height * 4;
143 | var pointer = module.alloc( byteSize );
144 | var buffer = new Uint8Array(mod.exports.memory.buffer, pointer, byteSize);
145 |
146 | var button = document.getElementById("run-wasm");
147 | var canvas = document.getElementById('screen');
148 | if (canvas.getContext) {
149 | var ctx = canvas.getContext('2d');
150 |
151 | var pointer = module.alloc( byteSize );
152 |
153 | var usub = new Uint8ClampedArray(mod.exports.memory.buffer, pointer, byteSize);
154 | var img = new ImageData(usub, width, height);
155 | var running = false;
156 |
157 | var frame = 0;
158 | var running = false;
159 | function step(timestamp) {
160 | if (!running) return;
161 |
162 | frame = module.fill(pointer, width, height, frame);
163 | ctx.putImageData(img, 0, 0)
164 |
165 | if (frame != 65536) {
166 | window.requestAnimationFrame(step);
167 | } else {
168 | button.innerText = "Restart";
169 | running = false;
170 | }
171 | }
172 |
173 | function clearCanvasAndRestart() {
174 | running = false;
175 | window.requestAnimationFrame(function() {
176 | ctx.clearRect(0, 0, width, height);
177 | module.clear(pointer, width, height);
178 | frame = 0;
179 | running = true;
180 | window.requestAnimationFrame(step);
181 | });
182 | }
183 |
184 | button.addEventListener("click", function(e) {
185 | if (running) {
186 | button.innerText = "Restart";
187 | running = false;;
188 | } else {
189 | button.innerText = "Stop";
190 | clearCanvasAndRestart();
191 | }
192 | });
193 | }
194 | });
195 |
196 |
--------------------------------------------------------------------------------
/demos/feistel/script.js:
--------------------------------------------------------------------------------
1 | fetch("feistel.wasm", {cache: "no-cache"}).then(response =>
2 | response.arrayBuffer()
3 | ).then(bytes =>
4 | WebAssembly.instantiate(bytes, {})
5 | ).then(results => {
6 | let module = {};
7 | let mod = results.instance;
8 | module.alloc = mod.exports.alloc;
9 | module.dealloc = mod.exports.dealloc;
10 | module.fill = mod.exports.fill;
11 | module.clear = mod.exports.clear;
12 |
13 | var width = 320;
14 | var height = 200;
15 |
16 | let byteSize = width * height * 4;
17 | var pointer = module.alloc( byteSize );
18 | var buffer = new Uint8Array(mod.exports.memory.buffer, pointer, byteSize);
19 |
20 | var button = document.getElementById("run-wasm");
21 | var canvas = document.getElementById('screen');
22 | if (canvas.getContext) {
23 | var ctx = canvas.getContext('2d');
24 |
25 | var pointer = module.alloc( byteSize );
26 |
27 | var usub = new Uint8ClampedArray(mod.exports.memory.buffer, pointer, byteSize);
28 | var img = new ImageData(usub, width, height);
29 | var running = false;
30 |
31 | var frame = 0;
32 | var running = false;
33 | function step(timestamp) {
34 | if (!running) return;
35 |
36 | frame = module.fill(pointer, width, height, frame);
37 | ctx.putImageData(img, 0, 0)
38 |
39 | if (frame != 65536) {
40 | window.requestAnimationFrame(step);
41 | } else {
42 | button.innerText = "Restart";
43 | running = false;
44 | }
45 | }
46 |
47 | function clearCanvasAndRestart() {
48 | running = false;
49 | window.requestAnimationFrame(function() {
50 | ctx.clearRect(0, 0, width, height);
51 | module.clear(pointer, width, height);
52 | frame = 0;
53 | running = true;
54 | window.requestAnimationFrame(step);
55 | });
56 | }
57 |
58 | button.addEventListener("click", function(e) {
59 | if (running) {
60 | button.innerText = "Restart";
61 | running = false;;
62 | } else {
63 | button.innerText = "Stop";
64 | clearCanvasAndRestart();
65 | }
66 | });
67 | }
68 | });
69 |
--------------------------------------------------------------------------------
/demos/import-memory/.gitignore:
--------------------------------------------------------------------------------
1 | target
2 |
--------------------------------------------------------------------------------
/demos/import-memory/Cargo.lock:
--------------------------------------------------------------------------------
1 | [[package]]
2 | name = "string-passing"
3 | version = "0.1.0"
4 |
5 |
--------------------------------------------------------------------------------
/demos/import-memory/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "string-passing"
3 | version = "0.1.0"
4 | authors = ["Jan-Erik Rediger
42 | Download string-passing.rs
43 |
44 | Download string-passing.wat (WebAssembly text format)
45 |
47 | The Rust code for this example does nothing but returning the data passed to it (plus some allocation functions). 48 | It uses one special attribute though: 49 | 50 |
#![wasm_import_memory]
51 |
52 | With this attribute the WebAssembly module will not export its own memory buffer,
53 | but instead import the memory from the environment, letting the caller set it up.
54 | In the future it will be possible to use a SharedArrayBuffer
in order to share memory between multiple modules easily.
55 |
56 |
57 | 58 | The code (without allocation functions) is: 59 |
60 | 61 |#![feature(wasm_import_memory)]
62 | #![wasm_import_memory]
63 |
64 | use std::os::raw::c_char;
65 |
66 | #[no_mangle]
67 | pub extern "C" fn roundtrip(data: *mut c_char) -> *mut c_char {
68 | data
69 | }
70 |
71 |
72 | The calling JavaScript code needs to instantiate a
73 | WebAssembly.Memory
object and pass it on instantiation.
74 |
window.Module = {}
77 |
78 | // Initializing the memory with 20 pages (20 * 64KiB = 1.25 MiB)
79 | const memory = new WebAssembly.Memory({initial: 20});
80 | const imports = {
81 | env: {
82 | memory: memory
83 | }
84 | };
85 |
86 | // On instantiation we pass the imports object
87 | fetchAndInstantiate("./string-passing.wasm", imports)
88 | .then(mod => {
89 | Module.memory = memory;
90 | Module.alloc = mod.exports.alloc;
91 | Module.dealloc = mod.exports.dealloc;
92 | Module.dealloc_str = mod.exports.dealloc_str;
93 | Module.roundtrip = function(str) {
94 | let buf = newString(Module, str);
95 | let outptr = mod.exports.roundtrip(buf);
96 | let result = copyCStr(Module, outptr);
97 | return result;
98 | };
99 |
100 | var output = document.getElementById("output");
101 | output.value = Module.roundtrip("This string was passed through WebAssembly")
102 | });
103 |
104 |
--------------------------------------------------------------------------------
/demos/import-memory/string-passing.rs:
--------------------------------------------------------------------------------
1 | #![feature(wasm_import_memory)]
2 | #![wasm_import_memory]
3 |
4 | use std::mem;
5 | use std::ffi::CString;
6 | use std::os::raw::{c_char, c_void};
7 |
8 | #[no_mangle]
9 | pub extern "C" fn alloc(size: usize) -> *mut c_void {
10 | let mut buf = Vec::with_capacity(size);
11 | let ptr = buf.as_mut_ptr();
12 | mem::forget(buf);
13 | return ptr as *mut c_void;
14 | }
15 |
16 | #[no_mangle]
17 | pub extern "C" fn dealloc(ptr: *mut c_void, cap: usize) {
18 | unsafe {
19 | let _buf = Vec::from_raw_parts(ptr, 0, cap);
20 | }
21 | }
22 |
23 | #[no_mangle]
24 | pub extern "C" fn dealloc_str(ptr: *mut c_char) {
25 | unsafe {
26 | let _ = CString::from_raw(ptr);
27 | }
28 | }
29 |
30 | #[no_mangle]
31 | pub extern "C" fn roundtrip(data: *mut c_char) -> *mut c_char {
32 | data
33 | }
34 |
--------------------------------------------------------------------------------
/demos/import-memory/string-passing.wasm:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/badboy/hellorust/f2dce7d25cd0a07b12a82c6738f205b1e67d8ee7/demos/import-memory/string-passing.wasm
--------------------------------------------------------------------------------
/demos/index.md:
--------------------------------------------------------------------------------
1 | permalink: /demos
2 | title: Demos
3 | layout: site.liquid
4 | data:
5 | route: demos
6 | ---
7 | ## Demos
8 |
9 | Some examples of Rust code compiled to JavaScript or WebAssembly.
10 | Code will be made published soon (but is more or less the default example with some JavaScript wrappers).
11 |
12 | * [Minimal working example](/demos/add/index.html)
13 | * [Calculate SHA1 digest](/demos/sha1/index.html)
14 | * [Factorial](/demos/factorial/index.html)
15 | * [Canvas](/demos/canvas/index.html)
16 | * [Import memory](/demos/import-memory/index.html)
17 | * [FizzleFade effect with a Feistel network](/demos/feistel/index.html)
18 | * [Call JavaScript from Rust](/demos/call-js/index.html)
19 |
--------------------------------------------------------------------------------
/demos/sha1/.gitignore:
--------------------------------------------------------------------------------
1 | target
2 |
--------------------------------------------------------------------------------
/demos/sha1/Cargo.lock:
--------------------------------------------------------------------------------
1 | [[package]]
2 | name = "sha1"
3 | version = "0.2.0"
4 | source = "registry+https://github.com/rust-lang/crates.io-index"
5 |
6 | [[package]]
7 | name = "sha1-digest"
8 | version = "0.1.0"
9 | dependencies = [
10 | "sha1 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
11 | ]
12 |
13 | [metadata]
14 | "checksum sha1 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "cc30b1e1e8c40c121ca33b86c23308a090d19974ef001b4bf6e61fd1a0fb095c"
15 |
--------------------------------------------------------------------------------
/demos/sha1/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "sha1-digest"
3 | version = "0.1.0"
4 | authors = ["Jan-Erik Rediger 8 | Type some text into the field and the SHA1 hash will be calculated. 9 |
10 | 11 |54 | The Rust code for this example is short and exposes a C-like API on top of the 55 | sha1 crate. 56 |
57 |
58 | Download sha1-digest.rs
59 |
60 | Download sha1-digest.wat (WebAssembly text format)
61 |
extern crate sha1;
64 |
65 | use std::mem;
66 | use std::ffi::{CString, CStr};
67 | use std::os::raw::{c_char, c_void};
68 |
69 | use sha1::Sha1;
70 |
71 | // In order to work with the memory we expose (de)allocation methods
72 | #[no_mangle]
73 | pub extern "C" fn alloc(size: usize) -> *mut c_void {
74 | let mut buf = Vec::with_capacity(size);
75 | let ptr = buf.as_mut_ptr();
76 | mem::forget(buf);
77 | return ptr as *mut c_void;
78 | }
79 |
80 | #[no_mangle]
81 | pub extern "C" fn dealloc(ptr: *mut c_void, cap: usize) {
82 | unsafe {
83 | let _buf = Vec::from_raw_parts(ptr, 0, cap);
84 | }
85 | }
86 |
87 | // The JavaScript side passes a pointer to a C-like string that's already placed into memory.
88 | // On the Rust side we turn this into a CStr, extract the bytes, pass it through the crate
89 | // and then turn it back into an memory-allocated C-like string.
90 | // A pointer to this data is returned.
91 | #[no_mangle]
92 | pub extern "C" fn digest(data: *mut c_char) -> *mut c_char {
93 | unsafe {
94 | let data = CStr::from_ptr(data);
95 |
96 | let mut m = Sha1::new();
97 | m.update(data.to_bytes());
98 | let dgst = m.digest().to_string();
99 | let s = CString::new(dgst).unwrap();
100 | s.into_raw()
101 | }
102 | }
103 |
104 |
105 |
106 | This can be built using cargo with the command cargo +nightly build --target wasm32-unknown-unknown --release
.
107 |
110 | The JavaScript code loads the WebAssembly module and has access to the exported functions and fields, including the allocation and memory.
111 | To pass a JavaScript string, we first allocate some space in the exported memory buffer and copy over the string.
112 | On return of the digest
function, we copy back the new string from the memory buffer.
113 |
var Module = {}
116 | var Sha1 = {
117 | digest: function(str) {
118 | let buf = newString(Module, str);
119 | let outptr = Module.digest(buf);
120 | let result = copyCStr(Module, outptr);
121 | Module.dealloc_str(buf);
122 | return result;
123 | }
124 | }
125 |
126 | fetchAndInstantiate("./sha1-digest.wasm", {})
127 | .then(mod => {
128 | Module.alloc = mod.exports.alloc;
129 | Module.dealloc = mod.exports.dealloc;
130 | Module.digest = mod.exports.digest;
131 | Module.memory = new Uint8Array(mod.exports.memory.buffer)
132 |
133 | var input = document.getElementById("input");
134 | var output = document.getElementById("output");
135 | input.addEventListener("keyup", function(e) {
136 | output.innerText = Sha1.digest(input.value);
137 | });
138 | });
139 |
140 |
--------------------------------------------------------------------------------
/demos/sha1/sha1-digest.rs:
--------------------------------------------------------------------------------
1 | extern crate sha1;
2 |
3 | use std::mem;
4 | use std::ffi::{CString, CStr};
5 | use std::os::raw::{c_char, c_void};
6 |
7 | use sha1::Sha1;
8 |
9 | #[no_mangle]
10 | pub extern "C" fn alloc(size: usize) -> *mut c_void {
11 | let mut buf = Vec::with_capacity(size);
12 | let ptr = buf.as_mut_ptr();
13 | mem::forget(buf);
14 | return ptr as *mut c_void;
15 | }
16 |
17 | #[no_mangle]
18 | pub extern "C" fn dealloc(ptr: *mut c_void, cap: usize) {
19 | unsafe {
20 | let _buf = Vec::from_raw_parts(ptr, 0, cap);
21 | }
22 | }
23 |
24 | #[no_mangle]
25 | pub extern "C" fn dealloc_str(ptr: *mut c_char) {
26 | unsafe {
27 | let _ = CString::from_raw(ptr);
28 | }
29 | }
30 |
31 | #[no_mangle]
32 | pub extern "C" fn digest(data: *mut c_char) -> *mut c_char {
33 | unsafe {
34 | let data = CStr::from_ptr(data);
35 |
36 | let mut m = Sha1::new();
37 | m.update(data.to_bytes());
38 | let dgst = m.digest().to_string();
39 | let s = CString::new(dgst).unwrap();
40 | s.into_raw()
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/demos/sha1/sha1-digest.wasm:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/badboy/hellorust/f2dce7d25cd0a07b12a82c6738f205b1e67d8ee7/demos/sha1/sha1-digest.wasm
--------------------------------------------------------------------------------
/index.liquid:
--------------------------------------------------------------------------------
1 | title: Latest News
2 | layout: site.liquid
3 | ---
4 | {{ post.content | strip_html | truncatewords: 25, '...' }}
15 |wasm32-unknown-unknown
target is natively available.
10 |
11 | Once you install that nightly (or any later one from now on), you can compile to WebAssembly without additional tools:
12 |
13 | ```
14 | rustup update
15 | rustup target add wasm32-unknown-unknown --toolchain nightly
16 | rustc +nightly --target wasm32-unknown-unknown -O hello.rs
17 | ```
18 |
19 | Support & documentation is still a bit limited, but we're working to expand in this area.
20 | The Rust compiler also does not have a proper linker just now, so final WebAssembly modules will be quite big.
21 | Alex wrote a small tool to reduce the size:
22 |
23 | ```
24 | cargo install --git https://github.com/alexcrichton/wasm-gc
25 | wasm-gc hello.wasm small-hello.wasm
26 | ```
27 |
28 | You can find the full instruction [in the `wasm-32-unknown-unknown` setup guide](/setup/wasm-target).
29 |
30 | If you want to contribute with examples, documentation, articles or other resources open an issue or pull request on [github.com/badboy/hellorust](https://github.com/badboy/hellorust).
31 |
--------------------------------------------------------------------------------
/news/post-1.md:
--------------------------------------------------------------------------------
1 | title: "Hello, Rust! Hello, WebAssembly!"
2 | published_date: "2017-11-18 15:10:00 +0100"
3 | layout: post.liquid
4 | data:
5 | author: "Jan-Erik Rediger"
6 | ---
7 | [hellorust.com](/) is live!
8 |
9 | This site will collect resources, guides, articles and links around Rust & WebAssembly.
10 | If you want to help or have more resources we should add, [open an issue](https://github.com/badboy/hellorust/issues/new) or send a Pull Request to [github.com/badboy/hellorust](https://github.com/badboy/hellorust).
11 |
12 | To get started, head over to the [Setup section](/setup/) or take a look at [some example code](/demos/).
13 |
--------------------------------------------------------------------------------
/resources.md:
--------------------------------------------------------------------------------
1 | permalink: /resources
2 | title: Resources
3 | layout: site.liquid
4 | data:
5 | route: resources
6 | ---
7 | ## Resources
8 |
9 | A collection of various resources and blog posts explaining Rust & WebAssembly, available tools or showing demo applications.
10 | You have somethings that's missing from this list? [Tell us about it!](https://github.com/badboy/hellorust/issues/new)
11 |
12 | ---
13 |
14 | ## Blog posts / Discussions
15 |
16 | * [State of WebAssembly and Rust?](https://internals.rust-lang.org/t/state-of-webassembly-and-rust/6077/51) (2017-10-19)
17 | Discussion on the current state with lots of answers from people using it
18 | * [The Path to Rust on the Web](http://asquera.de/blog/2017-04-10/the-path-to-rust-on-the-web/) (2017-04-10)
19 | Article by Andrew Hobden
20 |
21 |
22 | ## Projects
23 |
24 | * [Wargo](https://github.com/lord/wargo) - Easy Rust to WebAssembly
25 | * [stdweb](https://github.com/koute/stdweb) - A standard library for the client-side Web
26 | * [cargo-web](https://github.com/koute/cargo-web) - A Cargo subcommand for the client-side Web
27 |
28 |
29 | ## Relevant issues on the Rust issue tracker
30 |
31 | * [#36317: Initial webassembly support via LLVM](https://github.com/rust-lang/rust/issues/36317)
32 | Tracking issue for getting WASM support into rustc
33 | * [#45905: Add a new wasm32-unknown-unknown target](https://github.com/rust-lang/rust/pull/45905)
34 | A new Rust compile target for WebAssembly without Emscripten
35 |
--------------------------------------------------------------------------------
/robots.txt:
--------------------------------------------------------------------------------
1 | # empty on purpose
2 |
--------------------------------------------------------------------------------
/setup/docker.md:
--------------------------------------------------------------------------------
1 | permalink: /setup/docker
2 | title: "Setup - Docker"
3 | layout: site.liquid
4 | ---
5 | ## Setup
6 |
7 | Pull down tomaka's Docker image:
8 |
9 | ```
10 | docker pull tomaka/rustc-emscripten
11 | ```
12 |
13 | Start a shell and mount the current directory into it:
14 |
15 | ```
16 | docker run -it -v $(pwd):/src --rm tomaka/rustc-emscripten /bin/bash
17 | ```
18 |
19 | Your code is now accessible in `/src` and can be compiled from there:
20 |
21 | ```
22 | cd /src
23 | rustc --target wasm32-unknown-emscripten hello.rs
24 | ```
25 |
--------------------------------------------------------------------------------
/setup/emscripten.md:
--------------------------------------------------------------------------------
1 | permalink: /setup/emscripten
2 | title: "Setup - Emscripten"
3 | layout: site.liquid
4 | ---
5 | ## Setup
6 |
7 | To get a `rustc` with Emscripten support all you need is a recent version.
8 | You can install it using [rustup](http://rustup.rs/). Follow these steps:
9 |
10 | ```
11 | rustup toolchain add stable
12 | rustup target add asmjs-unknown-emscripten --toolchain stable
13 | rustup target add wasm32-unknown-emscripten --toolchain stable
14 | ```
15 |
16 | You still need the Emscripten SDK, see how to download and install it in detail or follow the simplified steps below (please refer to the official documentation for requirements and installation on Windows):
17 |
18 | ```
19 | wget https://s3.amazonaws.com/mozilla-games/emscripten/releases/emsdk-portable.tar.gz
20 | tar -xvf emsdk-portable.tar.gz
21 | cd emsdk_portable
22 | ./emsdk update
23 | ./emsdk install sdk-incoming-64bit
24 | ```
25 |
26 | Once everything is set up, you can compile with Emscripten:
27 |
28 | ```
29 | rustc --target asmjs-unknown-emscripten hello.rs
30 | ```
31 |
32 | You can also compile to WebAssembly instead:
33 |
34 | ```
35 | rustc --target wasm32-unknown-emscripten hello.rs
36 | ```
37 |
--------------------------------------------------------------------------------
/setup/index.md:
--------------------------------------------------------------------------------
1 | permalink: /setup/
2 | title: Setup
3 | layout: site.liquid
4 | data:
5 | route: setup
6 | ---
7 | ## Setup
8 |
9 | * [Setup `wasm32-unknown-unknown`](wasm-target/) *(recommended way, but more barebones)*
10 | * [Setup with Emscripten](emscripten/)
11 | * [Setup with Docker](docker/)
12 |
--------------------------------------------------------------------------------
/setup/wasm-target.md:
--------------------------------------------------------------------------------
1 | permalink: "/setup/wasm-target"
2 | title: "Setup - Wasm target"
3 | layout: site.liquid
4 | ---
5 | ## Setup
6 |
7 |
15 |
16 |
24 |
25 | Install the latest nightly (2017-11-25 or later):
26 |
27 | ```
28 | rustup toolchain install nightly
29 | ```
30 |
31 | If you already installed `nightly` before, make sure it is up to date:
32 |
33 | ```
34 | rustup update
35 | ```
36 |
37 | Install the required target:
38 |
39 | ```
40 | rustup target add wasm32-unknown-unknown --toolchain nightly
41 | ```
42 |
43 | Compile your code to WebAssembly:
44 |
45 | ```
46 | rustc +nightly --target wasm32-unknown-unknown -O hello.rs
47 | ```
48 |
49 | (The `+nightly` part is a shortcut handled by rustup's wrappers around `rustc` and `cargo`)
50 |
51 | You will end up with a `hello.wasm`. It will be quite big. To reduce size, first install `wasm-gc`:
52 |
53 | ```
54 | cargo install --git https://github.com/alexcrichton/wasm-gc
55 | ```
56 |
57 | Then run it to reduce the size of the WebAssembly module:
58 |
59 | ```
60 | wasm-gc hello.wasm small-hello.wasm
61 | ```
62 |
63 | You can also compile a project to WebAssembly using `cargo`.
64 | First create the project:
65 |
66 | ```
67 | cargo new myproject
68 | ```
69 |
70 | Next, change the crate type to `cdylib`. Add this to your `Cargo.toml`:
71 |
72 | ```
73 | [lib]
74 | path = "src/lib.rs"
75 | crate-type = ["cdylib"]
76 | ```
77 |
78 | Finally, compile it:
79 |
80 | ```
81 | cargo +nightly build --target wasm32-unknown-unknown --release
82 | ```
83 |
--------------------------------------------------------------------------------
/style.css:
--------------------------------------------------------------------------------
1 | body {
2 | font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
3 | margin-top: 20px;
4 | margin-bottom: 10px;
5 | max-width: 820px;
6 | }
7 | p {
8 | margin-top: 1.2em;
9 | margin-bottom: 1.2em;
10 | }
11 | .pitch b {
12 | font-weight: 400;
13 | }
14 | li {
15 | font-size:1.25em;
16 | }
17 | h1 {
18 | font-size: 2em;
19 | margin-top: 1.2em;
20 | margin-bottom: 0.5em;
21 | }
22 |
23 | ul {
24 | padding-left: 30px;
25 | }
26 | ul.menu {
27 | font-size: 1em;
28 | margin: 0;
29 | padding: 0;
30 | text-align: center;
31 | }
32 | ul.menu li {
33 | list-style-type: none;
34 | margin-top: 0em;
35 | margin-bottom: 0em;
36 | }
37 | ul.menu h2 {
38 | font-size: 20px;
39 | font-weight: 500;
40 | margin: 1em;
41 | display: inline;
42 | line-height: 1.5em;
43 | }
44 | ul.menu li>ul {
45 | margin-top: .5em;
46 | padding-left: 0em;
47 | line-height: 1.5em;
48 | }
49 |
50 | .menu img {
51 | margin: 0 auto 20px auto;
52 | }
53 | @media (min-width: 992px) {
54 | ul.menu li.menu {
55 | margin-top: 3em;
56 | }
57 | ul.menu {
58 | text-align: left;
59 | }
60 | .menu img {
61 | margin: 0;
62 | }
63 | }
64 | h1 {
65 | font-weight: 500;
66 | font-size: 2em; /* gridfit */
67 | }
68 | h2 {
69 | font-weight: 500;
70 | font-size: 1.75em; /* gridfit */
71 | }
72 | h3 {
73 | font-weight: 500;
74 | }
75 |
76 | .table-features {
77 | width: 100%;
78 | margin-bottom: 18px;
79 | border: 1px solid #ccc;
80 | border-radius: 3px;
81 | border-collapse: inherit;
82 | }
83 | .table-features td {
84 | padding: 6px 12px;
85 | border: none;
86 | }
87 |
88 | div.install {
89 | margin-top: 2.5em;
90 | margin-bottom: 1.5em;
91 | }
92 |
93 | .table-installers {
94 | border-spacing: 4px;
95 | border: 0px;
96 | empty-cells: hide;
97 | }
98 |
99 | .table-installers td {
100 | padding: 0px;
101 | }
102 |
103 | .table-installers td.inst-type {
104 | border: 0px;
105 | color: black;
106 | text-align: left;
107 | width: 13em;
108 | }
109 |
110 | .table-installers div.inst-button {
111 | border: 1px solid #ccc;
112 | text-align: center;
113 | border-radius: 3px;
114 | padding: 0.3em;
115 | }
116 |
117 | .table-installers div.inst-button:hover {
118 | border-color: #428BCA;
119 | color: white;
120 | background-color: #428BCA;
121 | }
122 |
123 | .table-installers a {
124 | text-decoration: none;
125 | }
126 |
127 | .install-row {
128 | margin-bottom: 0em;
129 | }
130 |
131 | p.pitch {
132 | font-size: 25px;
133 | font-weight: 300;
134 | text-align: center;
135 | }
136 | @media (min-width: 992px) {
137 | p.pitch {
138 | font-size: 25px;
139 | margin-top: 1.5em;
140 | margin-bottom: 1em;
141 | margin-right: 1em;
142 | text-align: left;
143 | }
144 | }
145 |
146 | p.pitch a {
147 | font-size: 80%;
148 | }
149 |
150 | .install-box {
151 | color: #777;
152 | text-align: right;
153 | font-size: 130%;
154 | margin-top: 0.8em;
155 | }
156 |
157 | .version-rec-box-inner {
158 | display: block;
159 | font-size: 12px;
160 | text-align: center;
161 | }
162 |
163 | hr {
164 | margin-top: 2em;
165 | margin-bottom: 3em;
166 | border-top: 2px solid #dedede;
167 | }
168 |
169 | .asterisk {
170 | margin-left: 0px;
171 | color: #428BCA;
172 | }
173 |
174 | .footnote {
175 | color: #777;
176 | text-align: right;
177 | font-size: 12px;
178 | padding: 0 15px;
179 | }
180 |
181 | .laundry-list {
182 | font-size: 16px;
183 | list-style-type: square;
184 | }
185 |
186 | .install-box a.btn {
187 | display: block;
188 | margin:1em auto;
189 | max-width: 250px;
190 | }
191 |
192 | ul.laundry-list {
193 | column-count: 2;
194 | -moz-column-count: 2;
195 | -webkit-column-count: 2;
196 | margin-bottom: 20px;
197 | }
198 |
199 | .resp-block {
200 | display: none;
201 | }
202 |
203 | @media (min-width: 992px) {
204 | .install-box a.btn {
205 | font-size: 18px;
206 | padding: 10px 15px;
207 | }
208 | ul.laundry-list {
209 | column-count: auto;
210 | -moz-column-count: auto;
211 | -webkit-column-count: auto;
212 | }
213 | img.ribbon {
214 | display: inline !important;
215 | position: absolute;
216 | top: 0;
217 | right: 0;
218 | border: 0;
219 | width: 149px;
220 | height: 149px;
221 | }
222 | ul.menu .col-xs-12 {
223 | padding-left: 0;
224 | }
225 | .resp-block {
226 | display: block;
227 | }
228 | }
229 |
230 | #editor {
231 | padding: none;
232 | margin: none;
233 | width: 100%;
234 | min-height: 340px;
235 | font-size: 13px;
236 | font-family: Menlo, Monaco, Consolas, "Courier New", monospace;
237 | white-space: pre-wrap;
238 | }
239 |
240 | #active-code {
241 | position: relative;
242 | display: none;
243 | padding: 10px;
244 | border-radius: 4px;
245 | background-color: #FDFDFD;
246 | border: 1px solid #CCC;
247 | }
248 |
249 | #run-code {
250 | position: absolute;
251 | z-index: 10;
252 | float: right;
253 | right: 8px;
254 | top: 8px;
255 | outline: none;
256 | }
257 |
258 | .icon-link-ext:before { content: '\e800'; } /* 'î €' */
259 |
260 | #result {
261 | background-color: #E2EEF6;
262 | margin-top: 10px;
263 | padding: 10px;
264 | display: none;
265 | border-radius: 4px;
266 | }
267 |
268 | .ace-error-text, .ace-error-line, .ace-warning-text, .ace-warning-line {
269 | position: absolute;
270 | }
271 |
272 | .ace-error-text {
273 | background-color: #e9abab;
274 | }
275 |
276 | .ace-error-line {
277 | background-color: #F6E2E2;
278 | }
279 |
280 | .ace-warning-text {
281 | background-color: #FFEF00;
282 | }
283 |
284 | .ace-warning-line {
285 | background-color: #FFFBCB;
286 | }
287 |
288 | .ace-chrome .ace_comment {
289 | color: #565656;
290 | }
291 |
292 | .ace-chrome .ace-keyword {
293 | color: #8959A8;
294 | }
295 |
296 | .ace-chrome .ace_entity.ace_name.ace_function {
297 | color: #4271AE;
298 | }
299 |
300 | .ace-chrome .ace_constant.ace_numeric {
301 | color: #718C00;
302 | }
303 |
304 | .ace-chrome .ace_keyword.ace_operator {
305 | color: black;
306 | }
307 |
308 | .ace-chrome .ace_string {
309 | color: #3E999F;
310 | }
311 |
312 | #active-code, #editor {
313 | background-color: #FAFAFA;
314 | }
315 |
316 | .more-examples {
317 | text-align: right;
318 | margin: 5px 10px 0 0;
319 | }
320 |
321 | .content {
322 | border-top: 2px solid #dedede;
323 | margin-top: 2em;
324 | margin-bottom: 8em;
325 | padding-top: 2em;
326 | }
327 |
328 | .content p,
329 | .content ul,
330 | .content ol,
331 | .content blockquote,
332 | .content pre {
333 | margin: 0 0 1rem;
334 | }
335 |
336 | .content h1 {
337 | font-size: 2.5em;
338 | line-height: 1.5em;
339 | margin-top: 0;
340 | margin-bottom: 1rem;
341 | font-weight: 400;
342 | position: relative;
343 | }
344 |
345 | .content h2 {
346 | font-size: 2em;
347 | line-height: 1.5em;
348 | margin: 3rem 0 1rem;
349 | font-weight: 400;
350 | border-top: 2px solid #dedede;
351 | padding-top: 1rem;
352 | }
353 |
354 | .content h3 {
355 | font-size: 1em;
356 | line-height: 1.5em;
357 | font-weight: 500;
358 | margin: .5rem 0;
359 | }
360 |
361 | .side-header h2 {
362 | font-weight: 500;
363 | font-size: 18.5px;
364 | line-height: 24px;
365 | margin-top: 7px;
366 | border-top: none;
367 | padding-top: 0;
368 | }
369 |
370 | .side-header h3 {
371 | font-size: 1em;
372 | margin-top: 0px;
373 | }
374 |
375 | .side-header p {
376 | color: #777;
377 | }
378 |
379 | @media screen and (min-width: 820px) {
380 | .faq {
381 | /*padding-right: 6em;*/
382 | }
383 |
384 | .faq p,
385 | .faq ul,
386 | .faq ol,
387 | .faq blockquote,
388 | .faq pre,
389 | .faq table {
390 | margin-left: 8rem;
391 | }
392 | }
393 |
394 | .faq .faq-intro {
395 | margin-left: 0rem;
396 | }
397 |
398 | .faq blockquote {
399 | border-left: .25em solid #dedede;
400 | padding-left: 2rem;
401 | }
402 |
403 | .faq blockquote p {
404 | margin: 0;
405 | }
406 |
407 | .faq pre {
408 | margin-top: 2rem;
409 | margin-bottom: 2rem;
410 | }
411 |
412 | .faq code {
413 | display: inline-block;
414 | padding: .1em .2em 0;
415 | font-size: 0.95em;
416 | line-height: 1em;
417 | background-color: #f7f7f7;
418 | border-radius: 2px;
419 | font-weight: 400;
420 | font-family: Consolas, "Andale Mono WT", "Andale Mono", "Lucida Console", "Lucida Sans Typewriter", "DejaVu Sans Mono", "Bitstream Vera Sans Mono", "Liberation Mono", "Nimbus Mono L", Monaco, "Courier New", Courier, monospace;
421 | }
422 |
423 | .faq pre code {
424 | font-size: 1em;
425 | line-height: 1.5em;
426 | }
427 |
428 | .faq h3 > a {
429 | color: black;
430 | }
431 |
432 | .faq ol ul {
433 | padding-left: 0px;
434 | margin-top: 0.5em;
435 | margin-left: 1em;
436 | }
437 |
438 | #toc h2 {
439 | border: 0;
440 | font-size: 2rem;
441 | line-height: 1.5em;
442 | }
443 |
444 | #toc .contents {
445 | -webkit-columns: 3 150px;
446 | -moz-columns: 3 150px;
447 | columns: 3 150px;
448 | -webkit-perspective: 1;
449 | }
450 |
451 | #toc ol {
452 | margin: 0 0 0 2rem;
453 | padding: 0;
454 | }
455 |
456 | #users > h1 {
457 | text-align: center;
458 | padding-top: 10px;
459 | font-size: 2.5em;
460 | font-weight: 400;
461 | margin: 0px;
462 | padding-top: 50px;
463 | }
464 |
465 | #users > h2 {
466 | text-align: center;
467 | font-size: 1.5em;
468 | line-height: 1.5em;
469 | font-weight: 400;
470 | margin: 0px;
471 | padding-bottom: 60px;
472 | }
473 |
474 | #users > div > div {
475 | text-align: center;
476 | }
477 |
478 | #users .user-container {
479 | margin-left: auto;
480 | margin-right: auto;
481 | width: 200px;
482 | height: 100px;
483 | }
484 |
485 | #users .user-container a {
486 | line-height: 100px;
487 | }
488 |
489 | #users img {
490 | max-width: 200px;
491 | max-height: 100px;
492 | vertical-align: middle;
493 | }
494 |
495 | #users .details {
496 | margin-top: 2em;
497 | margin-bottom: 2em;
498 | }
499 |
500 | #users .fade-in {
501 | transition: opacity 0.5s linear;
502 | }
503 |
504 | #users .fade-out {
505 | transition: opacity 0.1s ease-in-out;
506 | }
507 |
508 | #users .fade-in * {
509 | z-index: 1;
510 | }
511 |
512 | #users .fade-out * {
513 | z-index: -1;
514 | }
515 |
516 | @media (min-width: 992px) {
517 |
518 | #users .fade-in {
519 | opacity: 1;
520 | }
521 |
522 | #users .fade-out {
523 | opacity: 0;
524 | }
525 |
526 | }
527 |
528 | #users .user-details-row-1 div {
529 | height: 2em;
530 | margin-top: 0em;
531 | }
532 |
533 | #users .user-details-row-2 div {
534 | height: 2em;
535 | margin-top: -2em;
536 | }
537 |
538 | #users .user-details-row-3 div {
539 | height: 2em;
540 | margin-top: -2em;
541 | }
542 |
543 | #users .details p {
544 | margin: 0px;
545 | }
546 |
547 | #users em {
548 | font-weight: bold;
549 | }
550 |
551 | #users #user-add-info {
552 | text-align: center;
553 | padding-top: 50px;
554 | }
555 |
556 | .form input {
557 | font-size: 3em;
558 | width: 100%;
559 | }
560 |
561 | #output {
562 | font-size: 2.4em;
563 | width: 100%;
564 | word-wrap: break-word;
565 | }
566 | #number-out {
567 | font-size: 2.4em;
568 | }
569 |
570 | aside {
571 | border: 1px solid black;
572 | border-width: 1px 1px 1px 5px;
573 | padding: 5px;
574 | margin: 5px 0;
575 | }
576 | aside p {
577 | margin: 0.7em;
578 | }
579 |
580 | .deprecation-notice {
581 | text-align: center;
582 | background-color: #fffbdd;
583 | color: #735c0f;
584 | }
585 |
--------------------------------------------------------------------------------
/talks.md:
--------------------------------------------------------------------------------
1 | permalink: /talks
2 | title: Talks
3 | layout: site.liquid
4 | data:
5 | route: talks
6 | ---
7 | ## Talks & Workshops
8 |
9 | A collection of talks and workshops about Rust, WebAssembly and connected topics.
10 | You have a talk that's missing from this list? [Tell us about it!](https://github.com/badboy/hellorust/issues/new)
11 |
12 | ---
13 |
14 | ### WebAssembly for the Rest of Us
15 |
16 | **by Jan-Erik Rediger** at
17 | [JS Kongress Munich](https://2017.js-kongress.de/sessions/webassembly-rest-us/) &
18 | [Mozilla Dev Roadshow (Munich)](https://www.meetup.com/MuniCSS-finest/events/243276897/)
19 |
20 | When:
21 | 2017-11-14, 2017-11-15
22 |
23 | Material:
24 |
25 | * [Slides](https://fnordig.de/talks/2017/jskongress/)
26 | * [Video](https://www.youtube.com/watch?v=SGkZbxIGDNE)
27 |
28 | ---
29 |
30 | ### Interaction with real-world JavaScript from compiled Rust
31 |
32 | **by Ingvar Stepanyan** at
33 | [RustFest Kyiv 2017](http://2017.rustfest.eu/talks/#interaction-with-real-world-javascript-from-compiled-rust)
34 |
35 | Material:
36 |
37 | * [Video](https://www.youtube.com/watch?v=zxIbTfsOJZE)
38 |
39 | ---
40 |
41 | ### WebAssembly for the Rest of Us
42 |
43 | **by Jan-Erik Rediger** at
44 | [Codemotion Amsterdam](http://amsterdam2017.codemotionworld.com/) &
45 | [Mozilla Dev Roadshow (Düsseldorf)](https://beyondtellerrand.com/events/duesseldorf-2017/side-events/mozilla-roadshow)
46 |
47 | When:
48 | 2017-05-16, 2017-05-17
49 |
50 | Material:
51 |
52 | * [Slides](http://www.hellorust.com/codemotion-ams/slides/)
53 |
54 | ---
55 |
56 | ### Hire me for the JS, keep me for the Rust
57 |
58 | **by Jan-Erik Rediger** at [Rust Belt Rust 2016](http://www.rust-belt-rust.com/sessions/)
59 |
60 | When:
61 | 2016-10-27
62 |
63 | Material:
64 | * [Slides Talk](http://www.hellorust.com/emscripten/slides/rbr-talk)
65 | * [Slides Hands-on](http://www.hellorust.com/emscripten/slides/handson/)
66 | * [Demos](http://www.hellorust.com/emscripten/demos/)
67 |
68 | ---
69 |
70 | ### Compiling Rust to your Browser
71 |
72 | **by Jan-Erik Rediger** at [Rust Cologne/Bonn](https://www.meetup.com/Rust-Cologne-Bonn/events/233139845/)
73 |
74 | When:
75 | 2016-09-05
76 |
77 | Material:
78 | * [Slides](https://badboy.github.io/rust-to-the-browser/)
79 | * [Video](https://media.ccc.de/v/rustmcb.2016.09.compiling-rust-to-asmjs)
80 |
--------------------------------------------------------------------------------