[SOLVED] Make a plane always face the camera

Help appreciated! I would like a plane entity to always face the camera, rotating only on the Y axis. Like a billboard that twists 360 degrees as the user (camera) moves about the room. This only goes 180 then rotates in the opposite direction, disappearing (as the back of the plane isn’t rendered).

I know similar has been addressed in 2D, following mouse pointers; but I just can’t seem to wrap my head around this one in XR. It looks like getEulerAngles is resetting rotation, but I need the plane to rotate 90 degrees ‘upright’ first before facing the camera for the effect. Is there a way to do this? I tried with a parent, but still no avail. Do I use a tangent calculation with another method for rotation; any ideas please?

from faceCamera.js

var FaceCamera = pc.createScript('faceCamera');

// initialize code called once per entity
FaceCamera.prototype.initialize = function() {
     this.cameraParent = pc.app.root.findByName('Camera Parent');
     this.billParent = pc.app.root.findByName('BillParent');
     this.vecA = new pc.Vec3();

FaceCamera.prototype.update = function(dt) {
   this.vecA = this.entity.getEulerAngles();
   this.vecA.x = 90;

The simplified project is here and the issue can be viewed in an Oculus Quest as one uses the joysticks to move about the room.


Hi @Steveorevo,

You are on the right track, you just need to limit the axis that will be used for the look at.

Here is example code for a billboard that rotates only on Y:

var Billboard = pc.createScript('billboard');

// initialize code called once per entity
Billboard.prototype.initialize = function() {

    this.vec = new pc.Vec3();
    this.camera = this.app.root.findByName('Camera');

// update code called every frame
Billboard.prototype.update = function(dt) {

    this.vec.y = this.entity.getPosition().y;

For the 90 degrees rotation of the model in the local axis, you can easily do so in the editor by parenting the model to the billboard entity:

Otherwise you can use a rotateLocal(90, 0,0) after the last call in the code above.

Here is my example project: https://playcanvas.com/editor/scene/1317590

Thank you for the quick response. Applied the suggestion, but the entity doesn’t respond in VR… Maybe I need to sleep on it. :smiley:

Tried changes at

Okay, I was able to resolve this in the original Face Camera fork which I will leave up should anyone have a similar issue. This boiled down to simply using rotateLocal vs setLocalEulerAngles (thank you for your insight!). If anyone would like to explain how these differ (relative to graph node?); that would clarify a lot. Suffice it to say, it’s resolved. Slightly more complicated because I did use rotateLocal prior but couldn’t see results because:

  1. a plane render only renders one side; a 90 degree rotation made it always invisible; instead I needed to do a 270 degree local rotation.
  2. VR; it wasn’t until I’d put on the headset did I understand what was going on. This is because the desktop browser WebXR simulation plugin (for Firefox) has limited controls and the camera was a child of a parent. Changing things around for simulation or just putting on the headset helped reveal the issue.

All-in-all I thank you for the prompt response time and community PlayCanvas has put together here. Corrected code below and fixed in the original Face Camera cited repo. Marking this as solved. :slight_smile:

FaceCamera.prototype.update = function(dt) {
   this.entity.rotateLocal(270, 0, 0);
   // this.vecA = this.entity.getLocalEulerAngles();
   // this.vecA.x = 270;
   // this.entity.setLocalEulerAngles(this.vecA);
1 Like

Thanks for sharing your final solution!