Hi its been a long time since I wasn’t here. I was looking for how to improve performances my web that I’ve created before but I don’t know how to like improve performances in 3D scene cause I was use in basics and medium strategy… I don’t know where to research deep performances in advance 3D scene so…I’m using typescript here so its kinda messy I think?
I got this super high network in dev tools and I don’t like it.
And this is the website you can look for it for yourself.
I can show my full codes here if its fine. I just wanna know how to be able have a good performance both in desktop or cellphones…
This my MusicAmp.jsx that you can see in the music that does like spectrum.
import React, { useEffect, useRef, useMemo } from 'react';
import { useFrame } from '@react-three/fiber';
import * as THREE from 'three'
import { useAudio } from './music/AudioSpectrum';
const MusicAmp = () => {
const { analyser,bufferLength,dataArray,numBars,barRefs } = useAudio()
let barWidth:number = 0.005; // Adjusted barWidth
let gap:number = 0.020; // Adjusted gap
const smoothingFactor = 0.085;
useFrame(() => {
if (analyser.current && bufferLength.current > 0) {
analyser.current.getByteFrequencyData(dataArray.current);
const half = Math.ceil(numBars / 2);
let maxFrequency = 0;
for (let i = 0; i < half; i++) {
const barHeight = dataArray.current[i] / 256;
maxFrequency = Math.max(maxFrequency, barHeight);
const leftBar = barRefs.current[half - i - 1]?.current;
if (leftBar) {
const targetScaleY = barHeight * 3 || 0.1;
leftBar.scale.y += (targetScaleY - leftBar.scale.y) * smoothingFactor;
}
const rightBar = barRefs.current[half + i]?.current;
if (rightBar) {
const targetScaleY = barHeight * 3 || 0.1;
rightBar.scale.y += (targetScaleY - rightBar.scale.y) * smoothingFactor;
}
}
}
});
return (
<>
<group position={[0, 0, -0.2]}>
{Array(numBars)
.fill(null)
.map((_, i) => {
const half = Math.ceil(numBars / 2);
const position = i < half
? [
-(half - i) * (barWidth + gap) + gap / 2,
0.35,
0
]
: [
(i - half) * (barWidth + gap) + gap / 2,
0.35,
0
];
return (
<mesh key={i} ref={barRefs.current[i]} position={new THREE.Vector3(...position)}>
<boxGeometry args={[barWidth, 0.1, barWidth]} />
<meshStandardMaterial
color={0x3268a8}
toneMapped={false}
emissive={0xffffff}
emissiveIntensity={1}
/>
</mesh>
);
})}
</group>
</>
);
};
export default MusicAmp;
And this is the whole scene.
import * as THREE from 'three'
import { useState, useRef, Suspense, useMemo, useEffect, useLayoutEffect } from 'react'
import { Canvas, useThree, useFrame, useLoader, extend } from '@react-three/fiber'
import { OrbitControls, useTexture } from '@react-three/drei'
import { SVGLoader } from 'three/examples/jsm/loaders/SVGLoader'
import { EffectComposer, Bloom } from '@react-three/postprocessing'
import { RigProps } from '@/props/CanvasProps'
import { gsap } from 'gsap'
import Ground from './Ground'
import MusicAmp from './MusicAmp'
import { useAudio } from './music/AudioSpectrum'
function Diamonds({color, position,scale,highFrequency }:any) {
const ref = useRef<any>(null)
const { paths: [path] } : any = useLoader(SVGLoader, '/bloom/triangle.svg') // prettier-ignore
const geom1 = useMemo(() => SVGLoader.pointsToStroke(path.subPaths[0].getPoints(200), path.userData.style), [])
const { paths: [music] } : any = useLoader(SVGLoader, '/bloom/music.svg') // prettier-ignore
const geom2 = useMemo(() => SVGLoader.pointsToStroke(music.subPaths[0].getPoints(200), music.userData.style), [])
return (
<group ref={ref}>
<mesh
geometry={geom1}
position={position}
scale={scale}
>
<meshStandardMaterial
color={color}
toneMapped={false}
emissive={0xffffff}
emissiveIntensity={2}
/>
</mesh>
<mesh
geometry={geom2}
position={[position[0] + 0.05,0.9,position[2]]}
scale={scale}
rotation={[ Math.PI ,0,0]}
>
<meshStandardMaterial
side={THREE.DoubleSide}
color={color}
toneMapped={false}
emissive={0xffffff}
emissiveIntensity={2}
/>
</mesh>
</group>
)
}
function Circle({...props}:any) {
const { videoRef,loading,play } = useAudio()
const circleRef = useRef<any>(null);
useLayoutEffect(() => {
let g = circleRef.current
let z = 1.25
let p = g.parameters;
let hw = p.width * 0.8;
let a = new THREE.Vector2(-hw, 0);
let b = new THREE.Vector2(0, z);
let c = new THREE.Vector2(hw, 0);
let ab = new THREE.Vector2().subVectors(a, b);
let bc = new THREE.Vector2().subVectors(b, c);
let ac = new THREE.Vector2().subVectors(a, c);
let r = Math.round((ab.length() * bc.length() * ac.length()) / (2 * Math.abs(ab.cross(ac))));
// console.log(r)
let center = new THREE.Vector2(0, z - r);
let baseV = new THREE.Vector2().subVectors(a, center);
let baseAngle = baseV.angle() - (Math.PI / 2);
let arc = baseAngle * 2;
let uv = g.attributes.uv;
let pos = g.attributes.position;
let mainV = new THREE.Vector2();
console.log(uv)
console.log(pos)
for (let i = 0; i < uv.count; i++){
let uvRatio = 1 - uv.getX(i);
let y = pos.getY(i);
mainV.copy(c).rotateAround(center, (arc * (uvRatio)));
pos.setXYZ(i , mainV.x, y, -mainV.y);
}
pos.needsUpdate = true;
},[circleRef])
useEffect(() => {
if (videoRef.current && play) {
videoRef.current.volume = 0.2
videoRef.current.load();
if (loading && play) {
videoRef.current.play();
}
}
}, [loading]);
const texture = useMemo(() => {
if (videoRef.current) {
return new THREE.VideoTexture(videoRef.current);
}
}, [videoRef]);
return (
<>
<mesh
{...props}
>
<planeGeometry args={[2,2.5,32]}
ref={circleRef}
/>
<meshStandardMaterial
color={0xedebeb}
toneMapped={false}
map={texture}
side={THREE.DoubleSide}
/>
</mesh>
</>
)
}
function Rig({ children, loading }: RigProps) {
const ref = useRef<any>(null);
const vec = new THREE.Vector3();
const { camera, mouse } = useThree();
const [animationStarted, setAnimationStarted] = useState(false);
const lookAtStart = new THREE.Vector3(0, 1, 8);
const lookAtMid = new THREE.Vector3(1, 1, 0);
const lookAtEnd = new THREE.Vector3(0, 1, -8);
const lookAtSpline = new THREE.CatmullRomCurve3([lookAtStart, lookAtMid, lookAtEnd]);
const animationDuration = 4;
const elapsedTimeRef = useRef<number>(0);
useEffect(() => {
camera.lookAt(lookAtStart);
}, []);
useEffect(() => {
if (loading) {
const timer = setTimeout(() => {
setAnimationStarted(true);
}, 1000);
return () => clearTimeout(timer);
} else {
setAnimationStarted(false);
}
}, [loading]);
useFrame((state, delta) => {
if (loading && animationStarted) {
elapsedTimeRef.current += delta;
const t = Math.min(elapsedTimeRef.current / animationDuration, 1);
camera.position.lerpVectors(new THREE.Vector3(0, 1, 1.2), new THREE.Vector3(0, 1, 1.2), t);
const currentLookAt = lookAtSpline.getPoint(t);
camera.lookAt(currentLookAt);
if (ref.current) {
const clampedMouseY = mouse.y > 0 ? THREE.MathUtils.clamp(mouse.y, -Infinity, 0.5) : -0.5 ;
ref.current.position.lerp(vec.set(mouse.x * 0.02, clampedMouseY * 0.1, 0), 0.005);
ref.current.rotation.y = THREE.MathUtils.lerp(
ref.current.rotation.y,
(-mouse.x * Math.PI) / 100,
0.1
);
}
}
});
return <group ref={ref}>{children}</group>;
}
const Scene = () => {
const {loading,highFrequency} = useAudio()
const lightRef1 = useRef(null)
const { camera, gl }:any = useThree();
useEffect(() => {
const onWindowResize = () => {
if (camera) {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
gl.setSize(window.innerWidth, window.innerHeight);
}
};
window.addEventListener("resize", onWindowResize);
}, [camera, gl, camera]);
return(
<>
<color attach="background" args={['black']} />
<ambientLight ref={lightRef1} />
{/* <OrbitControls
// enableZoom={false}
// enablePan={false}
// enableRotate={false}
/> */}
<Suspense fallback={null}>
<Rig
loading={loading}
>
<Circle
position={[0,1,-0.4]}
/>
<MusicAmp />
<Diamonds
position={[-1.5,0.5,-0.4]}
scale={0.002}
highFrequency={highFrequency}
/>
<Diamonds
position={[1,0.5,-0.4]}
scale={0.002}
highFrequency={highFrequency}
/>
<Ground
position={[0,0,0]}
rotation={[- Math.PI / 2 , 0 , 0 ]}
/>
<Ground
position={[0,2.5,2.5]}
rotation={[- Math.PI , 0 , 0 ]}
/>
<Ground
position={[-2.5,2.5,0]}
rotation={[ 0 , Math.PI / 2 , 0 ]}
/>
<Ground
position={[-0,2.5, -2.5]}
rotation={[ 0 , 0 , 0 ]}
/>
<Ground
position={[2.5,2.5, 0]}
rotation={[ 0 , -Math.PI / 2 , 0 ]}
/>
</Rig>
<EffectComposer multisampling={4}>
<Bloom kernelSize={1} luminanceThreshold={1} luminanceSmoothing={0.4} intensity={0.5} />
</EffectComposer>
</Suspense>
</>
)
}
const Loaders = () => {
const { play,loading,playRef,pauseRef,setLoading,videoRef } = useAudio()
const refName = useRef<HTMLDivElement>(null)
const refSelectorName = useRef<HTMLDivElement>(null)
useEffect(() => {
let tl = gsap.timeline()
.to(refSelectorName.current,{
"--name-height":"100%",
duration:0.8,
ease:"power4.inOut"
})
.to(refName.current,{
translateX:0,
opacity:1,
duration:2,
ease:"power4.inOut"
})
},[])
const bricks = useRef<Array<HTMLSpanElement | null>>([null, null]);
useEffect(() => {
const tl = gsap.timeline({ repeat: 0 });
const totalDuration = 3; // Animation should complete in 5 seconds.
const totalProgress = 99; // 99 steps for 00% to 99%
const stepDuration = totalDuration / totalProgress;
for (let step = 0; step < totalProgress; step++) {
bricks.current.forEach((brick, index) => {
const digitIndex = 1 - index; // Reverse the index to make the last digit faster
const newNumber = Math.floor((step + 1) / Math.pow(10, digitIndex)) % 10;
if (brick) {
tl.to(
brick,
{
duration: stepDuration,
ease: 'none',
onComplete: () => {
brick.textContent = newNumber + '';
},
},
step * stepDuration
);
}
});
}
tl.to('.loader_display', { duration: 0.5, opacity: 0, display: 'none', ease: 'power2.in' }, totalDuration).then(() => {
if (!loading) {
setLoading(true);
}
});
}, []);
return (
<div className="loaders">
<video
ref={videoRef}
style={{ display: 'none' }}
src="./img/shelter-cut.mp4"
autoPlay
/>
<div className="name" ref={refSelectorName}>
<h4>
<span className="word" ref={refName}>
Julle Myth Vicentillo
</span>
</h4>
</div>
<div className="loader_display">
<h3 className="text">
{Array.from({ length: 2 }, (_, index) => (
<span key={index} className="load_text brick">
<span
className="slide"
ref={(el) => {
if (el) {
bricks.current[index] = el;
}
}}
>
{index === 0 ? '0' : '0'}
</span>
</span>
))}
<span className="load_text">%</span>
</h3>
</div>
<Canvas
className="loaders-composition"
camera = {{ position: [0,1,1.2] }}
shadows gl={{ antialias: true }}
>
<Scene />
</Canvas>
<div className="play-button" style={ loading ? { display:'flex' } : {display:'none'}}>
<svg
ref={playRef} width="24" height="24"
viewBox="0 0 24 24" fill="none"
xmlns="http://www.w3.org/2000/svg"
style={ play ? { display:'none' } : {display:"flex"}}
>
....
</svg>
<svg
ref={pauseRef} width="20" height="20"
viewBox="0 0 20 20" fill="none"
xmlns="http://www.w3.org/2000/svg"
style={ play ? { display:'flex' } : {display:"none"}}
>
....
</svg>
</div>
</div>
)
}
export default Loaders
And this is the wall and ground from both back to back and left to right sides of wall.
import { MeshReflectorMaterial, useTexture } from '@react-three/drei'
import { LinearEncoding, RepeatWrapping, TextureLoader } from 'three'
import * as THREE from 'three'
import React, { useEffect } from 'react'
import { useLoader } from '@react-three/fiber'
type GroundProps = {
rotation: number[];
position: number[];
}
const Ground: React.FC<GroundProps> = ({ position,rotation }) => {
const [roughness,normal] = useLoader(TextureLoader,[
"./bloom/surface_texture.jpg",
"./bloom/surface_normal.jpg",
])
useEffect(() => {
[normal,roughness].forEach((t) => {
t.wrapS = RepeatWrapping
t.wrapT = RepeatWrapping
t.repeat.set(5,5)
})
normal.encoding = LinearEncoding
},[normal,roughness])
return (
<>
<mesh
rotation={new THREE.Euler(...rotation)}
position={new THREE.Vector3(...position)}
>
<planeGeometry args={[5,5]}/>
{/* <boxGeometry args={[5,5,5]}/> */}
<MeshReflectorMaterial
envMapIntensity={0}
normalMap={normal}
roughnessMap={roughness}
dithering={true}
color={[0.015,0.015,0.015]}
roughness={0.4}
opacity={1}
blur={[1000,400]}
mixBlur={30}
transparent={true}
mixStrength={40}
mixContrast={1}
resolution={1024}
mirror={0}
side={THREE.DoubleSide}
depthScale={0.01}
minDepthThreshold={0.9}
maxDepthThreshold={1}
depthToBlurRatioBias={0.25}
reflectorOffset={0.2}
/>
</mesh>
</>
)
}
export default Ground
Do you think there is better code to make a proper performances in cellphone and desktop?