web-sys: canvas Julia set - The `wasm-bindgen` Guide

## Keyboard shortcuts

Press `←` or `→` to navigate between chapters

Press `S` or `/` to search in the book

Press `?` to show this help

Press `Esc` to hide this help

# Julia Set

View full source code or view the compiled example online

While not showing off a lot of `web_sys` API surface area, this example shows a neat fractal that you can make!

## `index.js`

A small bit of glue is added for this example

    import('./pkg')
        .then(wasm => {
            const canvas = document.getElementById('drawing');
            const ctx = canvas.getContext('2d');

            const realInput = document.getElementById('real');
            const imaginaryInput = document.getElementById('imaginary');
            const renderBtn = document.getElementById('render');

            renderBtn.addEventListener('click', () => {
                const real = parseFloat(realInput.value) || 0;
                const imaginary = parseFloat(imaginaryInput.value) || 0;
                wasm.draw(ctx, 600, 600, real, imaginary);
            });

            wasm.draw(ctx, 600, 600, -0.15, 0.65);
        })
        .catch(console.error);

## `src/lib.rs`

The bulk of the logic is in the generation of the fractal

    #![allow(unused)]
    fn main() {
    use std::ops::Add;
    use wasm_bindgen::prelude::*;
    use wasm_bindgen::Clamped;
    use web_sys::{CanvasRenderingContext2d, ImageData};

    #[wasm_bindgen]
    pub fn draw(
        ctx: &CanvasRenderingContext2d,
        width: u32,
        height: u32,
        real: f64,
        imaginary: f64,
    ) -> Result<(), JsValue> {
        // The real workhorse of this algorithm, generating pixel data
        let c = Complex { real, imaginary };
        let data = get_julia_set(width, height, c);
        let data = ImageData::new_with_u8_clamped_array_and_sh(Clamped(&data), width, height)?;
        ctx.put_image_data(&data, 0.0, 0.0)
    }

    fn get_julia_set(width: u32, height: u32, c: Complex) -> Vec<u8> {
        let mut data = Vec::new();

        let param_i = 1.5;
        let param_r = 1.5;
        let scale = 0.005;

        for x in 0..width {
            for y in 0..height {
                let z = Complex {
                    real: y as f64 * scale - param_r,
                    imaginary: x as f64 * scale - param_i,
                };
                let iter_index = get_iter_index(z, c);
                data.push((iter_index / 4) as u8);
                data.push((iter_index / 2) as u8);
                data.push(iter_index as u8);
                data.push(255);
            }
        }

        data
    }

    fn get_iter_index(z: Complex, c: Complex) -> u32 {
        let mut iter_index: u32 = 0;
        let mut z = z;
        while iter_index < 900 {
            if z.norm() > 2.0 {
                break;
            }
            z = z.square() + c;
            iter_index += 1;
        }
        iter_index
    }

    #[derive(Clone, Copy, Debug)]
    struct Complex {
        real: f64,
        imaginary: f64,
    }

    impl Complex {
        fn square(self) -> Complex {
            let real = (self.real * self.real) - (self.imaginary * self.imaginary);
            let imaginary = 2.0 * self.real * self.imaginary;
            Complex { real, imaginary }
        }

        fn norm(&self) -> f64 {
            (self.real * self.real) + (self.imaginary * self.imaginary)
        }
    }

    impl Add<Complex> for Complex {
        type Output = Complex;

        fn add(self, rhs: Complex) -> Complex {
            Complex {
                real: self.real + rhs.real,
                imaginary: self.imaginary + rhs.imaginary,
            }
        }
    }
    }