[{"data":1,"prerenderedAt":229},["ShallowReactive",2],{"blog-en-threejs-performance":3},{"id":4,"title":5,"body":6,"cover":213,"date":214,"description":215,"draft":216,"extension":217,"locale":218,"meta":219,"navigation":220,"path":221,"seo":222,"stem":223,"tags":224,"__hash__":228},"blog\u002Fblog\u002Fen\u002Fthreejs-performance.md","Three.js performance: smooth on every device",{"type":7,"value":8,"toc":209},"minimark",[9,13,18,21,27,35,173,184,188,195,202,205],[10,11,12],"p",{},"A 3D scene that runs at 120 frames per second on my development machine tells me little. The honest question is: how does it behave on a client's three-year-old smartphone? From the projects of recent years I've learned that performance in Three.js is rarely a question of raw compute — it's one of restraint.",[14,15,17],"h2",{"id":16},"fewer-draw-calls-more-calm","Fewer draw calls, more calm",[10,19,20],{},"The biggest lever is almost always draw calls. Every mesh drawn individually costs the CPU a round of conversation with the GPU. A thousand separate objects mean a thousand of those conversations per frame — and this is exactly where mobile devices break down.",[22,23,24],"blockquote",{},[10,25,26],{},"Performance is rarely a single trick. It's the sum of many small decisions you account for from the start.",[10,28,29,30,34],{},"When I need the same geometry a hundred times over — trees, particles, building blocks — I reach for ",[31,32,33],"code",{},"InstancedMesh",". A hundred draw calls become one.",[36,37,42],"pre",{"className":38,"code":39,"language":40,"meta":41,"style":41},"language-js shiki shiki-themes github-light github-dark","const mesh = new THREE.InstancedMesh(geometry, material, 1000);\nconst matrix = new THREE.Matrix4();\nfor (let i = 0; i \u003C 1000; i++) {\n  matrix.setPosition(positions[i]);\n  mesh.setMatrixAt(i, matrix);\n}\n","js","",[31,43,44,82,104,143,155,167],{"__ignoreMap":41},[45,46,49,53,57,60,63,66,70,73,76,79],"span",{"class":47,"line":48},"line",1,[45,50,52],{"class":51},"szBVR","const",[45,54,56],{"class":55},"sj4cs"," mesh",[45,58,59],{"class":51}," =",[45,61,62],{"class":51}," new",[45,64,65],{"class":55}," THREE",[45,67,69],{"class":68},"sVt8B",".",[45,71,33],{"class":72},"sScJk",[45,74,75],{"class":68},"(geometry, material, ",[45,77,78],{"class":55},"1000",[45,80,81],{"class":68},");\n",[45,83,85,87,90,92,94,96,98,101],{"class":47,"line":84},2,[45,86,52],{"class":51},[45,88,89],{"class":55}," matrix",[45,91,59],{"class":51},[45,93,62],{"class":51},[45,95,65],{"class":55},[45,97,69],{"class":68},[45,99,100],{"class":72},"Matrix4",[45,102,103],{"class":68},"();\n",[45,105,107,110,113,116,119,122,125,128,131,134,137,140],{"class":47,"line":106},3,[45,108,109],{"class":51},"for",[45,111,112],{"class":68}," (",[45,114,115],{"class":51},"let",[45,117,118],{"class":68}," i ",[45,120,121],{"class":51},"=",[45,123,124],{"class":55}," 0",[45,126,127],{"class":68},"; i ",[45,129,130],{"class":51},"\u003C",[45,132,133],{"class":55}," 1000",[45,135,136],{"class":68},"; i",[45,138,139],{"class":51},"++",[45,141,142],{"class":68},") {\n",[45,144,146,149,152],{"class":47,"line":145},4,[45,147,148],{"class":68},"  matrix.",[45,150,151],{"class":72},"setPosition",[45,153,154],{"class":68},"(positions[i]);\n",[45,156,158,161,164],{"class":47,"line":157},5,[45,159,160],{"class":68},"  mesh.",[45,162,163],{"class":72},"setMatrixAt",[45,165,166],{"class":68},"(i, matrix);\n",[45,168,170],{"class":47,"line":169},6,[45,171,172],{"class":68},"}\n",[10,174,175,176,179,180,183],{},"Where objects are static and don't need to move individually, I merge geometries instead — with ",[31,177,178],{},"mergeGeometries"," from ",[31,181,182],{},"BufferGeometryUtils",", many meshes become one. Sharing material instances rather than constantly creating new ones saves noticeably too.",[14,185,187],{"id":186},"cleaning-up-is-part-of-the-work","Cleaning up is part of the work",[10,189,190,191,194],{},"What many underestimate: Three.js doesn't clean up on its own. Geometries, materials, and textures live on in GPU memory until I explicitly release them with ",[31,192,193],{},".dispose()",". In single-page apps that swap scenes, I've seen memory leaks that crashed after minutes. Ever since, cleanup isn't an afterthought for me — it's part of every component.",[10,196,197,198,201],{},"For mobile devices I go a step further. I cap ",[31,199,200],{},"pixelRatio"," at 2, because feeding a retina display at factor 3 costs an enormous number of pixels. I detect weaker devices and reduce shadows, post-processing, and polygon count there. A scene doesn't have to look the same everywhere — it has to run smoothly everywhere.",[10,203,204],{},"In the end it's not about the maximum scene that runs somewhere. It's about the right scene that runs everywhere.",[206,207,208],"style",{},"html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":41,"searchDepth":84,"depth":84,"links":210},[211,212],{"id":16,"depth":84,"text":17},{"id":186,"depth":84,"text":187},null,"2023-05-18","Years with Three.js taught me that smooth 3D scenes aren't a matter of raw power, but of draw calls, instancing, and clean disposal of resources.",false,"md","en",{},true,"\u002Fblog\u002Fen\u002Fthreejs-performance",{"title":5,"description":215},"blog\u002Fen\u002Fthreejs-performance",[225,226,227],"Three.js","Performance","3D","klO67Nk7BdYneVCAjoYeSDfmCaHZSIL1lC5E2pgcADU",1781596072929]