Make 3D earth spin, along with CSS2DObjects

I have my a 3D earth, with markers/labels for specific locations added onto it. I want my earth to rotate, and it does. However, all of the CSS2DObjects (The markers/labels) start shaking as the earth rotates. I am guessing this is due to the objects being added on top of the earth, and they are not actually part of it spinning? Here is my code:

(function ($) {
$(document).ready(function () {

    /* Update/Add site values here, and they will display on earth view */
    var sites = [];

    //THREE JS setup variables
    var camera, scene, renderer, labelRenderer;

    var group;


    function init() {

        const EARTH_RADIUS = 225;
        const EARTH_SEGMENTS = 75;
        const EARTH_RINGS = 75;

        camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 2000);
        camera.position.set(-70, 375, 450);
        scene = new THREE.Scene();

        var light = new THREE.PointLight(0xffffff, 1, Infinity);


        var starGeometry = new THREE.SphereGeometry(1000, 50, 50);
        var starMaterial = new THREE.MeshPhongMaterial({
            map: new THREE.ImageUtils.loadTexture(basePath + 'Content/images/galaxy_starfield.png'),
            side: THREE.DoubleSide,
            shininess: 0
        var starField = new THREE.Mesh(starGeometry, starMaterial);

        //Create group to hold sphere and texture
        group = new THREE.Group();

        //Create sphere and texture, and mesh together using texture loader
        var loader = new THREE.TextureLoader();

        loader.load(basePath + 'Content/images/earth_no_clouds_16k.jpg', function (texture) {

            // Create the sphere
            var sphere = new THREE.SphereGeometry(EARTH_RADIUS, EARTH_SEGMENTS, EARTH_RINGS);

            // Map the texture to the material. 
            var material = new THREE.MeshBasicMaterial({
                map: texture
                //overdraw: 0.5,
                //transparent: true,
                //opacity: 0.9
            // Create a new mesh with sphere geometry.
            var mesh = new THREE.Mesh(sphere, material);

            // Add mesh to globe


        //Add Sites to Earth
        for (var i = 0; i < sites.length; i++) {

            if (sites[i].lat != null && sites[i].lon != null) {
                var name = sites[i].name;
                var lat = sites[i].lat;
                var lon = sites[i].lon;
                var status = sites[i].status;

                //Create dot for each site
                var siteDiv = document.createElement('div');
                siteDiv.className = 'dot';
                siteDiv.setAttribute("id", name);
                siteDiv.setAttribute("title", name);
                    status == "FMC" ? "green" :
                    status == "PMC" ? "yellow" : "red";

                //Create label to append to each site's dot
                var siteDivLabel = document.createElement('label');
                siteDivLabel.setAttribute("id", name + '_label');
                siteDivLabel.setAttribute("title", name + ' label');
                siteDivLabel.textContent = name;
                //Setting the label's class (Some labels need to display on different sides of the dot so that all can be seen in initial view.)
                if (name == 'BU' || name == 'COD') {
                    siteDivLabel.className = 'label_top_right label-default';
                } else if (name == 'FGA' || name == 'PAFB' || name == 'MDIOC') {
                    siteDivLabel.className = 'label_bottom_right label-default';
                } else if (name == 'CMD') {
                    siteDivLabel.className = 'label_bottom_left label-default';
                } else {
                    siteDivLabel.className = 'label_top_left label-default';

                var siteDot = new THREE.CSS2DObject(siteDiv);

                siteDiv.parent = siteDot;

       = "pointer";
                siteDot.element.onclick = function () { alert('clicked a site!') };

                var sitePosition = calcPosFromLatLonRad(lat, lon, EARTH_RADIUS);

                // Some code to manually move certain site markers so that they don't overlap each other too much.
                if (name == 'BO') {
                    siteDot.position.set(sitePosition[0] - 1, sitePosition[1] + 1, Math.abs(sitePosition[2]));
                } else if (name == 'CMD') {
                    siteDot.position.set(sitePosition[0] - 1, sitePosition[1] - 1, Math.abs(sitePosition[2]));
                } else if (name == 'PAFB') {
                    siteDot.position.set(sitePosition[0] + 1, sitePosition[1] - 6, Math.abs(sitePosition[2]));
                } else if (name == 'MDIOC') {
                    siteDot.position.set(sitePosition[0] + 5, sitePosition[1] + 4, Math.abs(sitePosition[2]));
                } else if (name == 'BU') {
                    siteDot.position.set(sitePosition[0] + 2, sitePosition[1] + 3, Math.abs(sitePosition[2]));
                } else {
                    siteDot.position.set(sitePosition[0], sitePosition[1], Math.abs(sitePosition[2]));

        renderer = new THREE.WebGLRenderer({ alpha: true });
        renderer.setClearColor(0xffffff, 0);
        renderer.setSize(window.innerWidth, window.innerHeight);

        labelRenderer = new THREE.CSS2DRenderer();
        labelRenderer.setSize(window.innerWidth, window.innerHeight); = 'absolute'; = 0;

        //For rotation and mouse controls
        var controls = new THREE.OrbitControls(camera, labelRenderer.domElement);

    function animate() {
        //group.rotation.y -= .0004;
        renderer.render(scene, camera);
        labelRenderer.render(scene, camera);

    /* Referenced from answer found here:
    function calcPosFromLatLonRad(lat, lon, radius) {

        var phi = (90 - lat) * (Math.PI / 180)
        var theta = (lon + 180) * (Math.PI / 180)

        x = -((radius) * Math.sin(phi) * Math.cos(theta))
        z = ((radius) * Math.sin(phi) * Math.sin(theta))
        y = ((radius) * Math.cos(phi))

        return [x, y, z];



Um, the code looks actually okay. Can you please try to reproduce the issue with the following live demo:

I’ve used the code from the official example. Just apply your changes, click on save and then share the updated fiddle in this topic.

Looking at the Moon label in the example you just posted, it also shakes a bit. Maybe it is just the text that shakes? The dots beneath the labels do not shake for me… It almost seems like my labels shake more than the example, here is a GIF (The dot colors look messed up in the GIF for some reason):


Link to GIF:

Um, I can’t see any shaking on my iMac using Chrome and Firefox. Maybe it is related to your browser?

In any event, the shaking in your animated GIF definitely looks strange. Please try to reproduce this glitch in the fiddle, otherwise I’m afraid we won’t make progress in this issue. The problem could be caused by all kinds of things…

I have updated my project, minus the map and star texture images, and I can still see the labels shaking as the earth rotates.

EDIT: I have also pushed this to a more powerful windows server, and still shows shaking. Could this be due to the size of my sphere or somehing? Maybe my camera positioning? I sort of just played around with those until things looked right…

This shaking is happening for me in both Chrome and Firefox, may even be worse in Firefox.