I was looking at my phone the other day—a device that is, by every metric, a technological miracle—and I watched it drop frames while I was scrolling through a list of settings.
It’s a strange feeling. We are walking around with devices more powerful than the high-end gaming rigs we built a decade ago. We have CPUs running at clock speeds that would have melted a motherboard in 2010. We have GPUs designed to crunch massive amounts of parallel data instantly.
And yet, my banking app stutters. My to-do list warms up the chassis like a hand warmer.
There’s this lingering misconception that when an app is slow, the hardware "just can't handle it." We instinctively blame the device. But the reality is that the hardware is screaming for work. It’s bored. The problem is that we, the developers, are feeding it junk.
The beast under the glass
To really get how absurd this is, you have to look at what these chips are actually doing when they aren't choking on a simple list view.
When you take a photo, your phone isn't just snapping a picture. In the fraction of a second after you tap the shutter, it grabs a dozen raw frames. It aligns them to fix your shaky hands. It analyzes noise patterns. It merges exposures for HDR. It runs ML models to find faces and sharpen eyes. It does all of this basically instantly.
It can run real-time voice transcription without touching the internet. It can render Genshin Impact—a massive open world—at high framerates.
If it can handle real-time AI video analysis, why can’t it animate a toggle switch without lagging?
A gut check on performance
I like to use a specific comparison to break people’s intuition on this, because our brains are bad at estimating computer complexity.
If you had to guess, which of these two tasks is harder for your phone to pull off in a single frame (1/60th of a second)?
Task A: Opening and displaying a single 5-megapixel JPEG photo.
Task B: Rendering a high-quality 3D model of a car (about 30k+ triangles) with lighting and textures.
Task A: 5MP JPEG Decoding (CPU)
Using jpeg-js to perform Huffman decoding and IDCT on the main thread.
Task B: 3D Model Render (GPU)
Loading and in-JS parsing an .obj file (~35K triangles) and rendering with WebGL. The GPU handles vertex transformations in parallel.
If you’re like most people, you pick the car. It looks "3D." It looks "expensive." The photo is just a flat image; surely that’s easy?
Actually, the photo is the performance killer.
Here is the math that nobody thinks about: A 5-megapixel photo has 5 million pixels. To show that image, the CPU has to chew through every single one of them. It has to do Huffman decoding (which is miserable to parallelize), run heavy math transforms (IDCT) to turn frequency data back into colors, and convert color spaces. It’s a massive, sequential slog through 5 million data points.
The 3D car? It’s only 35 thousand triangles.
Modern GPUs are built to eat triangles for breakfast. You tell the GPU "here are the dots," and it draws them in parallel instantly.
The 16-millisecond gap
Here is the part that keeps me up at night.
Your phone successfully decodes that massive 5MP image—doing all that I/O, memory allocation, and math—usually in less than 16 milliseconds. If you’re on a 120Hz screen, it often does it in less than 8 milliseconds.
It does millions of operations in the blink of an eye.
Yet, we regularly use text-based apps that can’t handle a button tap without a noticeable 200ms delay. We have hardware capable of folding proteins and rendering worlds, yet we are drowning in apps that stutter while trying to display a static list of strings.
The gap between what the hardware can do and what our code actually asks it to do is embarrassing.
"Death by a thousand re-renders"
So why is this happening? It’s mostly about how we build things now.
We’ve moved to tools like React Native because they let us move fast. We can write one codebase in JavaScript and ship it everywhere. And to be clear, React Native is great—especially with the New Architecture (Fabric and JSI) that lets JavaScript talk synchronously to the native side, ditching the old "bridge" bottleneck.
But that convenience is a double-edged sword.
In the old days, we blamed the bridge. Now, with synchronous communication, we have a new way to shoot ourselves in the foot: blocking the main thread.
It’s terrifyingly easy to write bad code. You make a "Settings" screen with 20 toggles. You put the state in the wrong place. You tap one switch, and suddenly the app decides it needs to recalculate the logic for the entire screen.
Inside that recalculation, maybe you put a date formatter or a list filter. It’s not "heavy" work compared to decoding a JPEG, but it’s running on the main thread. And because the new architecture allows synchronous updates, the UI freezes until that JavaScript is done.
You toggle a switch. The CPU spikes. The frame drops. The user thinks their phone is getting old.
The takeaway
It is easy to blame the silicon. We say "optimization is premature," or "phones are fast enough now."
But they are fast enough. They’ve been fast enough for years. When an app feels sluggish today, it’s rarely a hardware constraint. It’s usually because we are asking a supercomputer to perform the digital equivalent of digging a ditch with a teaspoon.
The bottleneck isn't the chip. It's the craftsmanship.