Blog

Multiple WebGL Canvases On a Single Page

2017-04-06

One time, somebody on the forum asked an interesting question about using several Canvas elements on a single page. Back then, I thought “Why would anybody need this?”. But after seeing many well-crafted applications, including ones from the well-known Animagraffs studio, I’m not so sure anymore. If you have doubts, then look into the demo page, for example, at the ISS 3D information graphics.

This intriguing feature provided to us by the Blend4Web developers deserves a close examination.

What’s the Point

If you try to initialize several Canvas elements in your script, you will naturally get an error, as the engine uses lots of its own variables to store operation conditions. This is understandable. So, in such cases, the developers recommend using the “namespace” concept.

Here is the idea of it: user’s web browser, as usual, loads one and only one engine file. However, you also set specific identifiers (namespaces) in your scripts, and these identifiers allow the engine to process your scripts as if they were separate units that work independently from each other.

A Bit of Practice

First, we will have to prepare a place for several Canvases in the HTML body. I will use a standard template that Project Manager generates when we create a new project.

So, here is the task: we need to place two independent Canvas elements on the page. Accordingly, let’s define DIV containers in the HTML file:

<body>
    <div id="canvas_container1"></div>
    <div id="canvas_container2"></div>
</body>

Let’s say that implemented code for different containers should be stored in different JavaScript files. Add the lines we need to the HTML:

<script type="text/javascript" src="canvas1.js"></script>
<script type="text/javascript" src="canvas2.js"></script>	

We also need to define location, size and other parameters in the CSS file:

#canvas_container1 {
  position: absolute;
  left: 0px;
  top: 0px;
  width: 640px;
  height: 480px;
}

#canvas_container2 {
  position: absolute;
  left: 0px;
  top: 500px;
  width: 640px;
  height: 480px;
}

And now comes the most interesting part. We only have to set the identifier while initializing the module in the b4w.require function so the engine would know to which namespace the script belongs. However, internal require function calls should not use any identifiers.

In the end, standard code generated by the Project Manager should look like this:

// register the application module
b4w.register("canvas1", function(exports, require) {

// import modules used by the app
var m_app       = require("app");
var m_cfg       = require("config");
...
b4w.require("canvas1","canvas1").init();

And the code for the second canvas should look like:

// register the application module
b4w.register("canvas2", function(exports, require) {

// import modules used by the app
var m_app       = require("app");
var m_cfg       = require("config");
...
b4w.require("canvas2","canvas2").init();

Interaction Between Two Canvases

Let’s take a look at a more complex example, where codes for different Canvas elements can interact with each other. Let’s try to start an animation on the first Canvas with a mouse click in the second Canvas window.

Define the external function “ext_method” for starting animation in the first Canvas code:

...
exports.ext_method = function() {
    var o = m_scs.get_object_by_name("cube");
    m_anim.apply_def(o);
    m_anim.play(o);
}
...

In the second Canvas code, define the animation function using require and set the identifier of the Canvas you need as a parameter. Note that in this case the require method is called from the global object b4w:

...
var m_ext_canv  = b4w.require("canvas1", "canvas1");
...
Add the standard code for processing “click” event:
...
function init_cb(canvas_elem, success) {

    if (!success) {
        console.log("b4w init failure");
        return;
    }
...
    canvas_elem.addEventListener("mousedown", main_canvas_click, false);
    canvas_elem.addEventListener("touchstart", main_canvas_click, false);
}

...
function main_canvas_click() {
    m_ext_canv.ext_method();
}

As you can see, there is nothing difficult in working with multiple Canvases, and still it opens the door to amazing opportunities!

Example source files.

Comments
06 apr. 2017 19:51
Very well done guys, truly amazing work and innovation. Love the annotation system with custom html ID. Ground breaking time.
02 jun. 2017 18:09
Thank you so much for great tutorial. I got this error when I run html file.

Uncaught Error: Module "__tbn" not found
at b4w.js:201
at b4w.module.__util (util.js:31)
at b4w.js:208
at b4w.module.__tsr (tsr.js:29)
at b4w.js:208
at b4w.module.__boundings (boundings.js:27)
at b4w.js:208
at b4w.module.__batch (batch.js:27)
at b4w.js:208
at b4w.module.__anchors (anchors.js:27)
b4w.js:201 Uncaught Error: Module "__tbn" not found
at b4w.js:201
at b4w.module.__util (util.js:31)
at b4w.js:208
at b4w.module.__tsr (tsr.js:29)
at b4w.js:208
at b4w.module.__boundings (boundings.js:27)
at b4w.js:208
at b4w.module.__camera (camera.js:28)
at b4w.js:208
at b4w.module.camera (camera.js:50)
b4w.js:201 Uncaught Error: Module "__tbn" not found
at b4w.js:201
at b4w.module.__util (util.js:31)
at b4w.js:208
at b4w.module.__tsr (tsr.js:29)
at b4w.js:208
at b4w.module.__boundings (boundings.js:27)
at b4w.js:208
at b4w.module.__camera (camera.js:28)
at b4w.js:208
at b4w.module.camera (camera.js:50)
02 jun. 2017 18:52
Hello and welcome to the forum!

Thank you so much for great tutorial. I got this error when I run html file.

Please press the check modules button:

And then press the Update Modules button. It should do the trick
02 jun. 2017 19:14
Great! Thanks again.
Please register or log in to leave a reply.