Jump to content
Search Community

High Main Thread Blocking Time and JavaScript Execution Time

SXLVX

Go to solution Solved by Cassie,

Recommended Posts

Posted

Hi GSAP Community,

I'm currently facing some performance issues with a project that I've been working on. According to Google PageSpeed Insights, I'm seeing significant main thread blocking time and high JavaScript execution time. Specifically, the report indicates:

  • Minimize the impact of third-party code: Third-party code blocked the main thread for 44,300 ms
  • Reduce JavaScript execution time: 25.1 s

I'm not sure if this is due to the way I have written my code or if there might be other underlying issues. Here is the JSFiddle for my project: JSFiddle

The Website / Test-Side which isnt published yet Test-Animation-Web

 

 

For context, my project involves animating multiple bars using Three.js and GSAP. Below is a brief overview of my setup:

  • I initialize the scene, camera, and renderer using Three.js.
  • I load textures and create bar objects.
  • I animate the bars using GSAP for both initial animation and continuous looping animation.

I am very new and inexperienced with GSAP, but I'm really proud of what I've achieved so far. 

Here is a snippet of my code:

let scene, camera, renderer, bars = [];
let barCount = 500; // Number of bars
let barWidth = 2 / barCount; // Width of the bars
let texture, noiseTexture;

function init() {
  const container = document.getElementById('container');

  // Initialize scene and camera
  scene = new THREE.Scene();
  camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
  camera.position.z = 1;
  camera.position.x = -2.5;

  // Initialize renderer
  renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true });
  renderer.setSize(window.innerWidth, window.innerHeight);
  renderer.shadowMap.enabled = true;
  container.appendChild(renderer.domElement);

  // Add lights
  const light = new THREE.PointLight(0xffffff, 1, 100);
  light.position.set(0, 5, 5);
  light.castShadow = true;
  scene.add(light);

  const ambientLight = new THREE.AmbientLight(0x404040);
  scene.add(ambientLight);

  // Load textures
  const loader = new THREE.TextureLoader();
  loader.load(
    'https://cors-anywhere.herokuapp.com/https://logton.de/lo-gt-on-sb92/wp-content/uploads/2024/07/DIE-LOGISTIKEXPERTEN-Logton.png',
    (tex) => {
      texture = tex;
      loader.load(
        'https://threejs.org/examples/textures/disturb.jpg',
        (noiseTex) => {
          noiseTexture = noiseTex;
          createBars(texture, noiseTexture);
          initialAnimation();
        }
      );
    },
    undefined,
    (err) => {
      console.error('Error loading texture:', err);
    }
  );

  window.addEventListener('resize', onWindowResize);
  onWindowResize();
  animate();
}

function createBars(texture, noiseTexture) {
  const aspectRatio = texture.image.width / texture.image.height;
  const barHeight = 1.7;

  for (let i = 0; i < barCount; i++) {
    let geometry = new THREE.PlaneGeometry(barWidth, barHeight);
    let material = new THREE.MeshStandardMaterial({
      map: texture,
      transparent: true,
      opacity: 0.9,
      onBeforeCompile: (shader) => {
        shader.uniforms.noiseTexture = { value: noiseTexture };
        shader.fragmentShader = `
          uniform sampler2D noiseTexture;
          ${shader.fragmentShader}
        `.replace(
          `#include <dithering_fragment>`,
          `
          vec4 noise = texture2D(noiseTexture, vUv);
          gl_FragColor.rgb += noise.rgb * 0.1;
          #include <dithering_fragment>
          `
        );
      }
    });

    let uvs = geometry.attributes.uv.array;
    for (let j = 0; j < uvs.length; j += 2) {
      uvs[j] = uvs[j] * (1 / barCount) + (i / barCount);
    }
    geometry.attributes.uv.needsUpdate = true;

    let bar = new THREE.Mesh(geometry, material);
    bar.position.x = (i - barCount / 2) * barWidth * aspectRatio;
    bar.position.y = Math.random() * 2 - 1;
    bar.scale.y = 10;
    bar.castShadow = true;
    bar.receiveShadow = true;
    bars.push(bar);
    scene.add(bar);
  }
}

function initialAnimation() {
  gsap.to(bars.map(bar => bar.position), {
    y: 0,
    duration: 2.5,
    ease: 'power4.inOut',
    stagger: 0.005
  });

  gsap.to(bars.map(bar => bar.scale), {
    y: 1,
    duration: 2.5,
    ease: 'power4.inOut',
    stagger: 0.005,
    onComplete: mainAnimation
  });
}

function mainAnimation() {
  const animationPhases = [
    { scaleY: 5, duration: 0.3, yOffset: 0.5 },
    { scaleY: 0.2, duration: 0.125, yOffset: 0.2 },
    { scaleY: 3, duration: 0.4, yOffset: 0.4 },
    { scaleY: 2, duration: 0.5, yOffset: 0.3 },
    { scaleY: 1.5, duration: 0.6, yOffset: 0.2 }
  ];

  let currentPhase = 0;

  function animatePhase() {
    const { scaleY, duration, yOffset } = animationPhases[currentPhase];

    bars.forEach((bar, i) => {
      const delay = Math.random() * 0.2;
      gsap.to(bar.position, {
        y: (Math.random() - 0.5) * yOffset,
        duration: duration,
        ease: 'power2.inOut',
        yoyo: true,
        repeat: 1,
        delay: delay,
        onComplete: () => {
          bar.position.y = 0;
        }
      });

      gsap.to(bar.scale, {
        y: Math.random() * scaleY + 1,
        duration: duration,
        ease: 'power2.inOut',
        yoyo: true,
        repeat: 1,
        delay: delay
      });
    });

    currentPhase = (currentPhase + 1) % animationPhases.length;
  }

  animatePhase();
  setInterval(animatePhase, 2000);
}

function onWindowResize() {
  camera.aspect = window.innerWidth / window.innerHeight;
  camera.updateProjectionMatrix();
  renderer.setSize(window.innerWidth, window.innerHeight);
}

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

function scrollContainer() {
  gsap.to(camera.position, {
    x: 2.5,
    duration: 16,
    ease: "linear",
    repeat: -1,
    onRepeat: () => {
      camera.position.x = -2.5;
    }
  });
}

init();
scrollContainer();

 

I would appreciate any insights or suggestions on how to improve the performance and reduce the blocking time and execution time. Thank you!

 

  • Solution
Posted

Hi there!

 

So there's always going to be a little bit of a compromise between performance and aesthetics when you add Three.js into a site, it's a hefty library for doing complex client side calculations and rendering.
 

 

My Advice would be to look into deferred loading if you don't need the three.js animation immediately, (although it actually looks like jQuery is your main culprit here)

https://developer.chrome.com/docs/lighthouse/performance/render-blocking-resources/?utm_source=lighthouse&utm_medium=lr

https://web.dev/articles/optimizing-content-efficiency-loading-third-party-javascript?utm_source=lighthouse&utm_medium=lr

 

Maybe try waiting until the DOM is loaded before animating

https://webmasters.stackexchange.com/questions/91129/does-google-value-domcontentloaded-or-load-time-more-as-a-ranking-signal

 

Performance is a deep topic and unfortunately this isn't really to do with how you're animating the scene with GSAP, that all looks fine! So we can't offer too much advice here

If you look on the pagespeed insights results there are links to guides to help you wherever there's an issue. That's going to be your best route!

 

  • Like 3
Posted

Hi Cassie,

Additionally, I want to express my gratitude for any help provided. I was unsure if my performance issues were due to using a loop instead of a timeline in GSAP, and your guidance has already helped clarify some of my doubts. Thank you so much for your support!

  • Like 3

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...