We cut our homepage script from 137 KB to 12 KB
The script behind our homepage animation used to be a 137 KB download. It is now 12 KB, roughly a tenth of the size, and the animation looks the same. We got there by replacing a large 3D library with a small renderer (the code that draws things on screen) written for this one scene. Our first attempt, trimming the library, did nothing at all. Below is what we did, why the obvious fix failed, and how to tell whether the same approach makes sense for your site.
What the homepage animation does
Our homepage runs a WebGL animation. WebGL is the part of the browser that draws graphics using the device’s graphics chip. About 70,000 particles on desktop (16,000 on phones) gather into the letters “VC”. As you scroll, they reshape into a browser window, then a phone, then a bar chart. At the end they burst outward into a tunnel, and you fly through it past panels showing our projects.
We first built it with three.js, a popular open-source 3D library. three.js is excellent. It handles lighting, 3D models, shadows, cameras and much more, and for most 3D work on the web it is the right place to start. It is also large, because it does all of those things.
The homepage script came to 547 KB. After compression, which is the shrunken form a server actually sends to the visitor, it was 137 KB. That compressed figure is the one that matters, because it is what a phone on a mobile connection has to download before the animation can start.
One point to be fair about: the script loads after the page text is already on screen and readable. It never blocked anyone from reading the content, even before this change. The goal was a lighter page overall, especially on phones.
The first attempt, which changed nothing
The standard advice for a big library is to import only the pieces you use. Modern build tools (the software that bundles your code for the browser) can often drop the parts of a library you never reference. So we listed exactly the named pieces our scene used and imported only those, expecting the file to shrink.
The size did not change at all. Not by a kilobyte.
The reason is how three.js is put together. Its renderer, the part that turns a scene into pixels, is one large piece. Any scene that draws anything needs it, and it was the bulk of the download. The build tool could remove small unused extras, but it could not remove the renderer, and the renderer was most of the file. Importing fewer names had no effect when the heaviest thing was the one we could not avoid importing.
This is worth knowing before you spend an afternoon on it. “Only import what you use” helps when a library is made of many independent parts. It does little when most of the weight sits in one core piece.
What the scene actually needed
So we wrote down what the animation really draws. The list was short:
- Points, for the particles.
- Lines, for the streaks in the tunnel and the outlines around the project panels.
- Flat rectangles with an image on them, for the project panels.
- Rings, for the portal at the end.
Just as useful was the list of what it never uses. No lighting. No 3D models. No shadows. No sorting of objects by depth, apart from drawing far things before near things so the see-through layers overlap correctly.
Most of what makes a general 3D library large was sitting unused on every page load.
A small renderer with the same names
We wrote a purpose-built renderer that does only those four things. It is a single file of a few hundred lines. The key decision was to give it the same function names our scene code was already calling from three.js: the same names for the scene, the camera, the particles, the materials and so on.
Because the names matched, the scene code barely had to change. For the most part the change was one line, the one that says where those names come from:
// before
import { Scene, Points, WebGLRenderer } from 'three';
// after
import { Scene, Points, WebGLRenderer } from './gl.js';
(The real import lists more names. This shortened version shows the idea.)
Everything else, the particle shapes, the scroll timing, the tunnel, stayed as it was. That kept the risk low. We were swapping the engine under the scene and leaving the scene alone.
The result: 29 KB, or 12 KB compressed, down from 137 KB. Roughly a tenth of the original download.
How we checked nothing changed
A smaller file is only a win if the page still looks right. We took screenshots of the old and new versions at the same scroll positions, at desktop and phone screen sizes, and compared them side by side. They matched.
There was one deliberate difference. The original drew the portal rings as thin 3D rings, like very skinny doughnuts. Ours draws them as flat rings that always face the camera. Because you only ever see them head-on as you fly toward them, they look the same from where you are.
What we gave up
This was a real trade-off, and it is worth being plain about it.
We now maintain custom rendering code instead of relying on a well-known library that a large community tests and fixes. If a browser update causes a problem, we fix it ourselves. There are no community answers to search for.
The renderer also supports only what this scene uses. If we later want lit 3D models or shadows on the homepage, we would have to extend it or bring a library back. That is fine for us, because the scene is settled. It would be a bad bet for a scene that is still changing.
When this is worth doing
Here is how we would weigh it for another project.
| Situation | Custom small renderer | Full library such as three.js |
|---|---|---|
| Scene uses a few simple shapes | Good fit | Works, but you ship features you never use |
| Scene needs lighting, models or shadows | Poor fit | Good fit |
| Page speed matters to the business | Worth considering | Check how much of the library you use first |
| Scene is still being designed | Poor fit | Good fit |
| Team is not comfortable with graphics code | Poor fit | Good fit |
It is worth it when the scene is simple and the page’s speed matters to the business, for example a homepage that most visitors reach on a phone, where a lighter page helps them get to the content. Google also uses page speed as one of its ranking signals, which is one reason it comes up in our technical SEO work, though a smaller script on its own will not move rankings.
It is not worth it when you need real 3D features, or when the people maintaining the site would struggle to look after custom rendering code. In those cases, use the library and accept the size. It is a fair price for a lot of well-tested work.
Whichever way you go, the useful habit is the one that started this: treat a library’s size as a cost, and check how much of it you actually use. You can do that yourself with your build tool’s size report before deciding anything. If you would like a hand with an animated site that still loads quickly, that is part of our web design and development work, and you can get in touch.