High cpu - fan blowing [solved: avoid mix-blend-mode difference]

Hi everyone,

I am experiencing a lot of problems with my website causing the fan of my laptop to blow. So somehow it is very CPU heavy, but I don’t know why. I’ve added a lot of networking code, but I commented all of that out for now. It is still cpu heavy and it is just displaying a textured cube + some FPS controls of the camera.

When I try out the pointerlock example on the three.js website it does not cause such a CPU problem.

I hope someone can give me tips or point me in the right direction.

Here is my website: http://webbb-dev.surge.sh/

Are you able to share your code? The code on the website is minified.

Hm strange.

When I open the website in Chrome and in devtools go to Sources > app.js. It is not minified…

chrome_2019-05-30_10-36-20

I can only see the bundle.js which is minified. Are you looking at the live website or a dev version?

I was looking at the live website, but maybe it cached my dev version files or something.

Anyway you can look at the source here:

Sorry I can’t make my project public due to safety concerns. I hope this app.js will give enough information:

import $ from 'jquery'; //jQuery
import 'webpack-jquery-ui';
import * as THREE from 'three'; //Three.js
import Controls from './controls'
import vertexShader from '../shaders/vertexShader'
import fragmentShader from '../shaders/fragmentShader'
import skyboxVertexShader from '../shaders/skyboxVertexShader'
import skyboxFragmentShader from '../shaders/skyboxFragmentShader'
import Gyroscope from './lib/Gyroscope'
import '../scss/main.scss'
import { networkManager } from './networkManager'
import metadrone1 from '../res/images/metadrone_screen_1.png'
import metadrone2 from '../res/images/metadrone_screen_2.png'
import metadrone3 from '../res/images/metadrone_screen_3.png'
import metadrone4 from '../res/images/metadrone_screen_4.png'
import maximova1 from '../res/images/maximova-1.png'
import maximova2 from '../res/images/maximova-2.png'
import maximova3 from '../res/images/maximova-3.png'
import proalerts1 from '../res/images/proalerts_1.png'
import metext from '../res/textures/me.png'

var scene, camera, controls, renderer, gyroscope, time = 0;

//start it up
initialiseScene();
setupScene();

//connect To the Server
networkManager.connectToServer();

//once connected setup player (camera) and start the loop
networkManager.localUserConnectedDisplayCallback = function(user){
    initialiseLocalUser(user)
    update();
}

//remote user connection callback
networkManager.userConnectedDisplayCallback = function(user){
    //setup remote user locally
    let metexture = new THREE.TextureLoader().load(metext)
    let userMaterial = new THREE.MeshBasicMaterial({map: metexture, side: THREE.DoubleSide})
    user.cube.material = userMaterial
    user.eyes.material = userMaterial
    scene.add(user.mesh)
    $('#canvas2D').append("<h1 id="+ user.userID +" class='playerName'>[" + user.userName + "]</h1>")
}

//chat input listener
$('#console-input').on('keyup', function(e){
    if (e.keyCode == 13) {
        if (networkManager.this_user) {
            var message = $('#console-input').val();
            networkManager.addChatMessage(message);
            $('#console-input').val("");     
        }            
    }
})

//network chat listener
networkManager.updatedChatMessagesDisplayCallback = function(message){
    $('#console-messages').append("<br>" + message);
    $('#console-messages')[0].scrollTop = $('#console-messages')[0].scrollHeight;  

    var html = $('#chat-inner-scroll').html()
    html = html + "<br>" + message
    $('#chat-inner-scroll').html(html)
    $('#chat-inner-scroll')[0].scrollTop = $('#chat-inner-scroll')[0].scrollHeight;
}

//SETUP --- 3D SCENE

function initialiseCamera(ratio) {
    camera = new THREE.PerspectiveCamera(70, ratio, 0.1, 4000 );
}

function initialiseScene() {
    scene = new THREE.Scene();
    scene.background = new THREE.Color( 0xffffff );
    
    let ratio = $('#canvas3D').innerWidth()/$('#canvas3D').innerHeight()
    initialiseCamera(ratio)

    renderer = new THREE.WebGLRenderer({canvas: document.getElementById('canvas3D'), antialias: true, alpha: false});
    renderer.debug.checkShaderErrors = true;
    resize();
}

function initialiseLocalUser(localUser) {
    localUser.mesh.add(camera)
    controls = new Controls(localUser.mesh)
    controls.movementSpeed = 300;
    localUser.controls = controls
    scene.add(controls.getObject());

    controls.yaw.translateX( (Math.random() > 0.5 ? -1 : 1) * (50 + (Math.random() * 50)));
    controls.yaw.translateZ( (Math.random() > 0.5 ? -1 : 1) * (50 + (Math.random() * 50)));
    controls.invertedYrot = controls.yaw.position.z < 0

    controls.yaw.lookAt(new THREE.Vector3());
    controls.yaw.rotateY(Math.PI)

    setupGyroscope(localUser.mesh)
}

function setupGyroscope(mesh) {
    gyroscope = new Gyroscope(mesh)
    gyroscope.onOrientationChange()

    $(window).on('deviceorientation', function(e) {
        if(gyroscope){
            gyroscope.onDeviceOrientation(e)
        }
    } );

    $(window).on('orientationchange', function(e) {
        if(gyroscope){
            gyroscope.onOrientationChange(e)
        }
    });

    if(window.screen.mozLockOrientation ) {

        window.screen.mozLockOrientation( 'portrait-primary' );
        
        window.screen.onorientationchange = function() {
            window.screen.mozUnlockOrientation();
            window.screen.mozLockOrientation( 'portrait-primary' );
        };
    
        document.addEventListener( 'visibilityChange', function(e) {
            if( !document.hidden ) {
                window.screen.mozUnlockOrientation();
                window.screen.mozLockOrientation( 'portrait-primary' );
            }
        });
    }
}

function setupSkyBox() {

    var light = new THREE.DirectionalLight( 0xdef1fc, 1.0 );
    light.position.x = 300;
    light.position.y = 250;
    light.position.z = - 500;
    scene.add( light );

    var skyGeo = new THREE.SphereBufferGeometry( 1000, 32, 15 );
    var skyMat = new THREE.ShaderMaterial( {
        uniforms: {
            topColor: { value: light.color }, //0xfff4d9
            bottomColor: { value: new THREE.Color( 0xffffff ) }, //0xfffBf0
            offset: { value: 400 },
            exponent: { value: 0.6 }
        },
        vertexShader: skyboxVertexShader,
        fragmentShader: skyboxFragmentShader,
        side: THREE.BackSide
    });

    var sky = new THREE.Mesh( skyGeo, skyMat );
    scene.add( sky );
}

function addObstacle() {
    let metexture = new THREE.TextureLoader().load(metext)
    let centerGeom = new THREE.BoxGeometry( 50, 50, 50);    
    let centerMaterial = new THREE.MeshBasicMaterial({map: metexture, side: THREE.DoubleSide})
    let center = new THREE.Mesh( centerGeom, centerMaterial ); 
    center.translateX(0)
    center.translateZ(0)
    scene.add(center)
}

function setupScene() {
    //setupSkyBox()
    addObstacle()
    var light = new THREE.AmbientLight( 0x66747c, 1.0 );
    let color = new THREE.Color().setRGB(0.903,0.912,0.965)
    var directionalLight = new THREE.DirectionalLight(color, 1.0 );
    directionalLight.position.set(-10, 10, 80);
    scene.add( directionalLight );      
    scene.add( light );
}

//UPDATE
let canvas = document.getElementById('canvas3D')
let clock = new THREE.Clock();
var previous = new THREE.Vector3();
function update() {
    let delta = clock.getDelta()
    time += delta

    networkManager.time = time
    //networkManager.updateUserNetworkOrientation()

    // if(gyroscope) {
    //     gyroscope.update()
    // }

    // var moved = !previous.equals(controls.yaw.position)
    // console.log(controls.didmove);
    // //console.log(controls.yaw.position)

    // $.each(networkManager.users, function(index, user){
    //     user.lerpOrientation(delta, camera, canvas, controls.didmove)
    //     if($("#"+user.userID).length) {
    //         if(user.nameVector !== null) {
    //             $("#"+user.userID).css({'left':user.nameVector.x + 'px',
    //             'top':user.nameVector.y + 'px'})
    //         }
    //     }
    // })

    //if( !$('#console-input').is(':focus')){
        controls.update(delta)
    //}

   // previous.copy(controls.yaw.position);
    renderer.render(scene, camera)
    requestAnimationFrame( update )
}

// ---- JQUERY STUFF ----

$('#console').resizable({ ghost: true, handles: 's'});

function resize() {
    let width = window.innerWidth;
    let height = window.innerHeight;
    camera.aspect = width/height;
    camera.updateProjectionMatrix();
    renderer.setSize(width, height);
    $("body, html").css({width:width, height:height})
    calculateScrollArrows()
}

function calculateScrollArrows() {
    var maxHeight = $("#menu-box-outer").innerHeight() - $("#menu-scroll").innerHeight()
    var offset = $("#menu-scroll").offset().top - $("#menu-box-outer").offset().top
    let isIos = navigator.userAgent.match("/(iPod|iPhone|iPad)/")

    if($("#menu-box-outer").innerHeight() > $("#menu-scroll").innerHeight()){
        if(offset == 0){
            $('#menu-scroll-arrow-down').css("visibility", isIos ? "hidden" : "visible")
            $('#menu-scroll-arrow-up').css("visibility", isIos ? "visible" : "hidden")
        } else if(offset == maxHeight){
            $('#menu-scroll-arrow-down').css("visibility", isIos ? "visible" : "hidden")
            $('#menu-scroll-arrow-up').css("visibility", isIos ? "hidden" : "visible")
        } else {
            $('#menu-scroll-arrow-down').css("visibility","visible")
            $('#menu-scroll-arrow-up').css("visibility","visible")
        }
    } else {
        $('#menu-scroll-arrow-down').css("visibility","hidden")
        $('#menu-scroll-arrow-up').css("visibility","hidden")
    }
}


$("#menu-scroll").scroll( function(){
    calculateScrollArrows()
})

$(window).on('resize', function() {
    resize();     
})

$(window).on('unload', function() {
    networkManager.removeUser()
    scene.remove(networkManager.this_user.mesh)
})

$(window).on('beforeunload', function() {
    networkManager.removeUser()
    scene.remove(networkManager.this_user.mesh)
})

$(window).on('pagehide', function() {
    networkManager.removeUser()
    scene.remove(networkManager.this_user.mesh)
})

$(window).on('load', function() {
    resize();
    $('#metadrone-1').attr('src', metadrone1)
    $('#metadrone-2').attr('src', metadrone2)
    $('#metadrone-3').attr('src', metadrone3)
    $('#metadrone-4').attr('src', metadrone4)
    $('#maximova-1').attr('src', maximova1)
    $('#maximova-2').attr('src', maximova2)
    $('#maximova-3').attr('src', maximova3)
    $('#pro-alerts-1').attr('src', proalerts1)
})

$('#grid').on('click', function(){
    $('#content-container').css({"visibility":"hidden"})
})

$('.menu-item h1').on('click', function(e){
    let idx = $(this).parent().index()
    scrollToContentItem(idx)
    e.stopPropagation();
})

$('#about-button').on('click', function(e){
    let idx = $('#content').children().length - 1
    scrollToContentItem(idx)
    e.stopPropagation();
})

function scrollToContentItem(idx) {
    let offset = ($('.content-item').eq(idx).offset().top - $('#content').offset().top) + 1
    $('#content-container').css({"visibility":"visible"})
    $('#content-scroll').animate({
        scrollTop: offset
    }, 10, function() {
        $('#content-container').css({"visibility":"visible"})
    });
}

$('#close-button').on('click', function(){
    console.log("yoooo clicked")
    $('#content-container').css({"visibility":"hidden"})
})

$('#cheats').on('click', function(){
    alert("type '#chname ...' to change your name\ntype '#rain' to make it rain");
})

Nothing super bad pops out at me immediately, but I have noticed that you set the far parameter to 4000 when creating the camera, which is a lot. Maybe lower than to say, 200? See if that helps. Also try turning of anti-aliasing when creating the renderer.

Thank you for the tips!

Unfortunately it is not really improving the CPU intensity. Even if I leave my controls untouched, the fan is still blowing hard. I will look further into it.

Thanks again.

Strange. What’re the specs of your laptop?

MacBook Pro (Retina, 13-inch, Early 2015)
Processor: 2,7 GHz Intel Core i5
Memory: 8 GB 1867 MHz DDR3
Graphics: Intel Iris Graphics 6100 1536 MB

Small correction: it only starts to heat up when I move around with the controls. The problem is it never cools down once I stop moving around.

What happens on your device if you run a simple examples like this one for a period of time:

https://threejs.org/examples/webgl_geometry_cube.html

I hear the fan warming up. Not as much as when running my website though.

I did another experiment. I commented out the ‘Obstacle / Cube’ in the scene. Pure emptyness now. After moving around with the controls for a while I get the same intensity of the fan again…

Ok I tried the pointerlock example one more time and after some minutes the fan does hit the same intensity as my website. So I guess it is due to my pc specs. :frowning:

Then again, I am only rendering one cube instead of hundreds as in the pointerlock example. Shouldn’t that make a difference? And also even when I comment out my cube and move around in emptyness it is still heating up. So is the three-simple-fp-controls possible to blame for this on its own?

I was thinking a 2015 macbookpro should be able to handle this. Maybe my rendering code is running on the cpu instead of gpu? By default it is gpu right?

Excuse me for all the questions, but you guys really help :slight_smile: Thanks!

I’ve reduced my update method to the following:

function update() {
    renderer.render(scene, camera)
    requestAnimationFrame( update )
}

my scene is empty.

My fan is still heating up!! I tried profiling in the chrome dev tools and indeed the cpu amount intensifies but I cannot figure out what is going on. :frowning:

I found it!

And yes, it is actually related to my specs, BUT I believe the problem remains somewhat interesting and can be seen as something to keep in mind or to avoid. The problem is that I have an overlay div for all my UI stuff (html/css/ non-three.js). In this UI I am making extensive use of the css property “mix-blend-mode: difference” to make a difference blend with the background (the three-js canvas).

Since the three.js canvas renders ±60 frames a second, it is probably applying this effect that many times.

Maybe this is something obvious for experienced three.js devs, but for a beginner I guess I had to learn it the hard way :slight_smile:

Glad I found it. I do want to keep the effect so now the question is: how to solve this?

Cheers!

1 Like

Maybe this is something obvious for experienced three.js devs, but for a beginner I guess I had to learn it the hard way :slight_smile:

No, it’s something that has to be learned by everyone the hard way I think. Some CSS effects are simple and some are really expensive, and it’s not obvious which.

Thnx!

I’m glad this post turned into something useful for three-js devs after all. Not just a my-specs-are-too-bad problem :stuck_out_tongue:

1 Like