Shader Adventures: a runner tool

By Victor Gridnevsky
Jan. 24, 2020

Hello. λ.meetup #2 happened less than a week ago and there is a tiny thing I want to share with all of you.

It is yet another tool to run your shader code outside of ShaderToy and similar sites.
I made it so I can use it in deck.js for my talks and I plan to improve it from time to time. For example, it lacks WebGL2 support, but I intend to fix that (upd: I already started working on an update and I want to polish it). After all, WebGL2 is just better.

It is based on threejsfundamentals code, but I think I made a convenient wrapper.

It is really easy to use. To start, you need a CSS to extend the canvas element to the browser's viewport and a piece of JavaScript.

Let's assume your canvas id is shader_canvas and look at the CSS part.

body {
    margin: 0;
}

#shader_canvas {
    width: 100vw;
    height: 100vh;
    display: block;
}

Now, regarding the JavaScript part, we create a shaderRunner instance, pass it a canvas selector and fragment shader text, prepare it, and call render.

A prepare function receives no arguments (I will consider removing it later). Then you start the animation by using a render function, which needs a starting time

let runner = new ShaderRunner({
    selector: 'canvas#shader_canvas',
    fragmentShader: document.querySelector('script#fragmentShader').text
});
runner.prepare();
runner.render(0.0);

But wait, there's more! I used this tool in a presentation for my talk, so I added a method to send variables inside the GLSL context. Each slide animation is placed inside a separate HTML file using iFrame tags, so you need to alter your code like this:

let runner = new ShaderRunner({
    selector: 'canvas#shader_canvas',
    fragmentShader: document.querySelector('script#fragmentShader').text
});
runner.prepare();
runner.setUniform('tilt', 0.0);
runner.render(0.0);

window.addEventListener('message', receiver, false);

function receiver(e) {
    if (e.origin == '*') {
        return;
    }
    else {
        if (e.data == 'tilt') {
            runner.setUniform('tilt', 1.0);
        } else {
            runner.setUniform('tilt', 0.0);
        }
    }
}

Since we are altering a tilt property, we need this declaration at our shader text (before the function that uses it):

uniform float tilt;

Now, to pass the message to the iFrame element, we write something like this:

document.querySelector("iframe#frameid").contentWindow.postMessage(message, '*');

You can check out the demo right now.

Alternatively, download the full code using this link.

Thank you for reading, that’s all for now.