Technique for 2D Hotspots and 2D interface


I’ve been looking at the techniques @will showed in this project…

… and there are two feature I’m trying to implement. The first one is having a menu and a logo and the second one are the hotspots. With the first goal, to include a menu and a logo I figured the most straightforward solution was to add HTML elements to the document which seems to work very well.

Hotspots on the other hand, seems to present a few different possible paths. When reading through this forum I’ve identified at least these three different techniques:

  1. Using 3D planes with a texture together with a billboarding script to have them facing the camera at all times. The can either penetrate other geometry or disregard the depth buffer, but in my tests none of those solutions seems perfect. (much like in this project
  2. Using a 2D canvas and drawing objects directly onto it, similar to the iphone project.
  3. and third, creating HTML dom elements, like an image or div or similar, and style and position that with css much like in the bmw i8 project.

In my case, a requirement is that the solution should be able to have an interactiveness so that when you click on a hotspot, a certain lightbox style information should popup for example.

I’ve tried version 1 but I’m not happy with the intersecting thing. So yesterday I tried version 2 from the iphone project but I ran into some problems. Perhaps version 3 is the way to go but I still haven’t tried it.

About version 2, drawing on the 2 canvas, I added this code to create the canvas:

// Create the HTML canvas on which to draw hotspot images/points.
    var app =;
    this.canvas = document.createElement('canvas');
    this.canvas.width = app.graphicsDevice.width;
    this.canvas.height = app.graphicsDevice.height; = 'absolute'; = '0px'; = '0px';


    this.ctx = this.canvas.getContext('2d');

… But when I do, and add it to the body element, I see the performance of my camera orbiting gets slowed down by a lot. It’s not as responsive but more sluggish when turning the model. Also, it seems to create something “selectable” spanning the entire screen, so sometimes when dragging or accidently doubleclicking - the entire screen gets “selected” and gets a purple overlay (in chrome).

Also, I’m not sure how to hide these 2D elements, whether using solution 2 or 3 from the 3D models when the hotspots appear on the back side.

Anyone experienced that can shine a light on the pros and cons and common pitfalls about these 3 techniques, or perhaps there is forth one I’ve missed.

Best regards

  • Björn

Regarding option 1, could you expand more on what you were not happy with?

Well, I ended up creating these two versions where one hotspot was ignoring the depth channel and one was honoring it. The one honoring it was correctly hidden when behind the object, but instead intersects with the objects. The one ignoring it on the other hand get annoying because it is showing like an xray through ALL objects. I guess I want something kind of inbetween.

See this project:

I’m halfway there with a solution. (just updated this at 16:30)

Basically what I have done is create a parent object to the hotspot itself and if the forward axis of the parent is facing away from the camera (using a dot product), it hides the hotspot.

This fixes the issue of seeing the hotspot through a mesh on one specific direction but you still can get that x-ray effect depending on the viewing angle :confused:

With some careful placement of the hotspots and directions, it can work for this situation and additionally you can fade in/out the hotspots based on the angle to the camera.

Otherwise I would think I need to make ‘visibility’ planes so hotspots won’t appear if a one of these planes is in between the camera and the hotspot

Regarding option #1:
Mentioned already in few other places: do not do depthTest and depthWrite for materials that shall be drawn over other things.

Regarding option #2, make sure you do use ctx.beginPath before each primitive drawing. And yes - it is expensive to have another 2D canvas over main canvas.

Thank you @steven, that is genius! I’ve had time to understand what the dot product operation does now. I haven’t been programming for 15 years, but just been designing, rendering and animating :slight_smile:

Now that I understand what’s going on I see that this technique with the dot product is also used in was used in @will 's iphone project, and I guess perhaps on the BMW i8 aswell.

Then technically all three solutions would be able to do pretty much the same thing. What would you say are then the pro’s and con’s about them all? Perhaps performance differences or interface bugs/quirks.

@max I did use that beginPath before drawing my circle and text object, but still the canvas is “double-clickable” in my tests.

Here I managed to optimized the code to this pretty simple row, this time trying it out on a HTML element (hence the CSS reference style.opacity).

// If the object is facing away from the camera, then hide it
// If the dot product is less than 0.2 start hiding it. Dot product range 0.2 -> 0, is mapped to opacity 1.0 -> 0.
var dotProduct =; = "" + Math.max ( Math.min (1.0, (dotProduct*5)), 0.0);

Modifying style on each frame - is pretty unrecommended from performance reasons.
Fastest option would be full 3D way.

I prefer DOM way though :slight_smile:
In i8 it is done using class add/remove to the element, only when the state of visibility needs to be changed.
That performs much better. And animation (transition) would be controlled simply from CSS, and be smooth.

But you got the dot product idea, it is pretty simple and nice to use :slight_smile:

I see, well then perhaps I should re-think again because I’ll have about 10 of those hotspots. Each hotspot should also spark a lightbox/fancybox popup. This should be achievable both in the 3D path and the DOM path no?

I’ve ruled out the 2D canvas path right now.


  • Björn

You could do a hybrid option, something that is dynamic - actual augmented bubbles I would do as 3D things.
And fancybox popup as DOM :slight_smile:

1 Like