Particles running TERRIBLE on Chrome

I’ve written this code which is running very smoothly in Safari (30fps on a 4K screen & 2018 Intel MacBook Pro). It also runs 60fps on my iPhone, and on Chrome for Windows. It’s also running at 60fps on Chrome on an M1 MacBook.

However, when I try to run it on Chrome (105.0.5195.125) on my Intel Mac, it basically hangs!

I’ve seen plenty of old 1m+ particle demos that run fine on Chrome, so I’m assuming I’ve done something dumb? I did try to downgrade to webGL1.0 and it ran about 20fps on Chrome. I also tried Chrome Canary, but it still hangs.

Can anyone please help me?

Here is my code:

import * as THREE from "three";
import Stats from "three/examples/jsm/libs/stats.module.js";

const canvas = document.querySelector("canvas");
const w = document.documentElement.clientWidth;
const h = document.documentElement.clientHeight;

const scene = new THREE.Scene();

const stats = new Stats();
document.body.appendChild(stats.dom);

//RENDERER
const renderer = new THREE.WebGLRenderer({canvas: canvas, antialias: false});
renderer.powerPreference = "high-performance";
renderer.setSize(w, h);
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setClearColor(0x000000, 1);

//CAMERA
const camera = new THREE.PerspectiveCamera(45, 1, 1, 1000);
camera.position.set(0, 0, 2);
camera.lookAt(new THREE.Vector3(0, 0, 0));
camera.fov = 2 * (180/Math.PI) * Math.atan(1 / 4);
camera.aspect = w / h;
camera.updateProjectionMatrix();
scene.add(camera);


// const mat = new THREE.MeshBasicMaterial({color: 0xff0000});
// const geo = new THREE.PlaneGeometry(camera.aspect, 1);
// const mesh = new THREE.Mesh(geo, mat);
// scene.add(mesh);


//SHADER
const shaderVert = `

precision mediump float;

uniform float time;

uniform mat4 modelViewMatrix;
uniform mat4 projectionMatrix;

attribute vec3 position;
attribute vec4 offset;
attribute vec2 uv;

varying vec3 vPosition;
varying float vTime;

void main ()
{
	vPosition = offset.xyz;

	vTime = sin(time + offset.w);

	float scale = 0.001;

	//BILLBOARD
	gl_Position = projectionMatrix * (modelViewMatrix * vec4(vPosition, 1.0) + vec4(position.xy * scale, 0.0, 0.0));
}

`;

const shaderFrag = `

precision mediump float;

varying float vTime;

void main ()
{
	vec4 color = vec4(vTime, vTime, vTime, 1.0);

	gl_FragColor = color;
}

`;

//BUFFERGEOMETRY
const COUNT = 1000 * 1000;
const TAU = Math.PI * 2;
let step = 0;

const positions = [];
const uvs = [];
const offsets = [];

positions.push( 0.5, -0.5, 0.0);
positions.push( 0.5,  0.5, 0.0);
positions.push(-0.5,  0.5, 0.0);
positions.push( 0.5, -0.5, 0.0);
positions.push(-0.5,  0.5, 0.0);
positions.push(-0.5, -0.5, 0.0);

uvs.push(1.0, 0.0);
uvs.push(1.0, 1.0);
uvs.push(0.0, 1.0);
uvs.push(1.0, 0.0);
uvs.push(0.0, 1.0);
uvs.push(0.0, 0.0);

for (let i = 0; i < COUNT; i++)
{
	//Create offsets
	offsets.push(
		Math.random() - 0.5, 
		Math.random() - 0.5, 
		Math.random() - 0.5, 
		Math.random() * TAU
	);
}

const geometry = new THREE.InstancedBufferGeometry();
geometry.instanceCount = COUNT;

geometry.setAttribute("position", new THREE.Float32BufferAttribute(positions, 3));
geometry.setAttribute("uv", new THREE.Float32BufferAttribute(uvs, 2));
geometry.setAttribute("offset", new THREE.InstancedBufferAttribute(new Float32Array(offsets), 4));

//MATERIAL
const material = new THREE.RawShaderMaterial(
{
	uniforms: {
		"time": { type: "f", value: 1.0 }
	},
	vertexShader: shaderVert,
	fragmentShader: shaderFrag
});


const mesh = new THREE.Mesh(geometry, material);
scene.add(mesh);

//RENDER LOOP
function render ()
{
	step += 0.1;
	material.uniforms["time"].value = step;

	renderer.render(scene, camera);

	stats.update();

	requestAnimationFrame(render);
}
render();

Can you put the code in a CodePen / JSFiddle so others can run it and see if we have the same performance issue?

Is there a reason for not using points for your particles? Points should render faster than sprites, if they take the same size on screen.

Thanks for your reply!

Here’s a pen: https://codepen.io/highlyinteractive/full/oNdpJGm

The reason I’m not using points is because I’m animating the UV values (not shown in this test). I don’t think that’s possible with points?

You are running into a known Chromium bug: 1245448 - chromium - An open-source project to help move the web forward. - Monorail

This can be easily proved since your app runs well with WebGL 1: Edit fiddle - JSFiddle - Code Playground

No, point clouds do not support texture coordinates.

I had a hunch that would be the issue. Thanks for the help!