Is it possible to make a FreeCamera follow physically a mesh like the FollowCamera does?

I need your help.

I’m trying to recreate something like this website → (press on discover the experience)

What I need to do, as the reference shows, is make the camera follow a path on scroll and still make the camera rotate around the point in which it stops, if the user drags.

Since the animation could be very complex, I wanted to make a “ghost cube” in the model and animating its position and rotation, then export the whole model and work on the animationGroups already made in the scene.

I experimented the different cameras but it seems like I can’t make it work right with none of them.
In my scene the FreeCamera, when I set as lockedTarget the cube mesh, it just looks at it, without following, while the effects it makes when dragging is right, cause it rotates around the point as if it were a first person pov.
The follow camera, when I set its lockedTarget as the cube mesh, and I put:

camera.radius = 0;
camera.heightOffset = 0;
camera.rotationOffset = 0;

it follows the mesh correctly, preserving the animation (rotation + position) I made on the external model, but when I drag it just changes the height offset and gets far away from the target, without rotating around point.
Also, the arcFollowCamera seems not the right one to use because it also doesn’t pivots around a point but has other rotation logic.

From this experiment it looks like the FreeCamera might be the right one as first person perspective, while the FollowCamera is the right one as following the mesh behaviour.

I tried to make a playground, but I don’t think it’s really effective because it’s too simple, there are no reference in the space and the animation is not imported but set as key frames. So it differs from my situation.

Babylon.js Playground (drag a little to see the scene moving)

I noticed a difference between this playground and my project, here the FreeCamera, when I set its position as the target position, it keeps following, while on my project it doesn’t follow the mesh only by setting that property.

Any help or suggestions about how to proceed to get this result, is really appreciated.
Thank you very much

It sounds like FreeCamera makes the most sense here with an animation on its position.

It’s a bit hard to help because you haven’t shown any code. My guess is that you haven’t linked the “ghost cube” to your camera correctly. Have you tried using scene.onBeforeRenderObservable and updating the camera’s position every frame?

If you are using glTF, you might also be able to achieve this result using glTF cameras and animating them in the DCC tool. glTF cameras loaded into Babylon.js basically behaves like you described.

Here is an example using the VC sample from the glTF sample models.

1 Like

Hi. You can create different cameras and switch between this cameras in every moment. Before switching you can set same position, rotation and other properties of original camera. As example followCamera and freeCamera, move mesh with animation, following with followCam and switch to freeCam when animation end. Take in account if you animate camera properties directly you loose camera controls on canvas. Better way animate transformPoints and attach camera and target to this points and animate this points and on animation end detach camera from this transform points

1 Like

Hi @ariwoot !

I am using this technique in my game I am working on in my spare time. It’s a chase camera. You can click and drag to suspend the movement of the cube and rotate around it. Once you release the mouse button the chasing continues.

Hope this helps!


EDIT: you can set the pivot to offset the camera to any position at line 33 and change the lerp value at line 66 to set the chasing speed


Hello @ariwoot just checking in, was your question answered?


hey @carolhmj, thank you for checking in and sorry for the delayed response but I haven’t logged into Babylon in two weeks.

the answers surely triggered some ideas, but i had to follow other ways because my project is a bit different from the cases and examples showed above.

I’ll explain my solution to achieve the result similar to cause maybe it can help someone in the future, even though i’m aware it still has some flaws and it’s not even close to the museum fluidity and experience feeling.

Here’s the process in a list of steps:

  1. I created a div tag (#scroller) with a height in pixels that will act as scroller. The more pixels, the more it’ll take to play all the animation.

  2. I created a FreeCamera and placed it at the starting point of my animation path.

  3. I detached the keyboard controls cause I want the user to be forced to follow the track.

  4. I generated a timeline using GSAP library - i’m used to make all the animated part of my website through it, so I usually prefer using it instead of babylon keyframes, but that’s just a habit and preference.
    This timeline uses as scrollTrigger the #scroller div and it animates manually the position and rotation of the camera. This is one of the parts that concern me the most, because it a very mechanic procedure. Also it doesn’t uses curves (like catmull rom splines or cubic ecc…) cause i didn’t know how to integrate that logic with my timeline. So it’s a path made of straight lines, instead of smooth curves.
    What I aim to do in the next project is to import an animation from blender (made through a bezier curve) and make the camera follow it’s path, but I really couldn’t make it in this one… I still need to study a lot.
    Example of the gsap animation:

 this.timeline = gsap.timeline({
      scrollTrigger: {
        id: "scroller",
        trigger: "#scroller",
        start: "top top",
        end: "bottom bottom",
        scrub: 2
    .to(, {x: 110, duration: 100})
    .to(, {y: 1.87, duration: 5}, '-=100')
    .to(, {y: 1.27, duration: 10}, '-=95')
    .to(, { y: 3, duration: 10}, '-=80') 
    .to(, { y: 1.27, duration: 10}, '-=70') 
    .to(, { y: 2, duration: 10}, '-=60') 
    .to(, { y: 1.27, duration: 10}, '-=50') 
    .to(, { y: 1, duration: 10}, '-=40') 
    .to(, { y: 0, duration: 10}, '-=30') 
    .to(, { y: -1, x: -0.3, duration: 10}, '-=20') 
    .to(, {z: 6, duration: 10}, '-=88')
    .to(, {z: 7, duration: 5}, '-=78')
    .to(, {z: -3, duration: 8}, '-=73')
    .to(, {z: -11, duration: 16}, '-=65')
    .to(, {z: -9, duration: 5}, '-=49')
    .to(, {z: -11, duration: 5}, '-=44')
    .to(, {z: -7, duration: 10}, '-=30')
    .to(, {z: -2, duration: 10}, '-=20')
  1. So now, when the user scrolls, the camera follows the timeline. When the user stops scrolling, the camera stays still. This behavior happens forwards and backwards.

  2. I also had to add the possibility to look around by dragging when the user is not scrolling. They had to be able to rotate around the position in which they were at the moment. So I listened to PointerEventTypes.POINTERDOWN and I stored the actual camera rotation and set as true a variable isDragging.

  3. Now, if the user starts scrolling again, I had to reposition the camera on the previous track position and proceed with the normal path. So in the scroll event listener, if the variable isDragging, I called a repositionCamera() function that animates the rotation of the camera from the result of dragging, to the previous camera rotation stored. As a consequence, the user is brought back to the track and if they scroll, the go back into the path set in the gsap timeline.

This is, in broad terms, the procedure I coded to achieve that reference.
I know it’s still kind of mechanic and could be reaaally improved.

I’ll work on it, but, if in the meanwhile someone wants to join me and suggest me some improvements, it’ll really be appreciated.

Thank you to everybody :sun_with_face:

1 Like