DEV Community

Cover image for The Illusion of Waves: When “Looks Right” Isn’t “Built Right” ft. VibeCodeArena
YASHWANTH REDDY K
YASHWANTH REDDY K

Posted on

The Illusion of Waves: When “Looks Right” Isn’t “Built Right” ft. VibeCodeArena

There’s something fascinating about challenges that feel visual but are actually deeply mathematical underneath. The “Ripple Wave Visualizer” prompt sits exactly in that category. At first glance, it sounds like a UI problem—draw some circles, animate them, make it pretty. But the moment you start thinking about what a ripple actually is, you realize you’re not building a UI anymore. You’re attempting to simulate a physical phenomenon.

And that’s where things got interesting.

Two models took on this challenge. Both produced working outputs. Both rendered ripples on a canvas. Both had controls, interactions, and animation loops. But under the surface, they reveal two very different interpretations of the same problem—and more importantly, two different limitations of AI-generated code.

Let’s unpack this properly.

What Was Asked vs What Was Built

The prompt wasn’t vague. It explicitly asked for:

Ripples behaving like real waves

Overlapping waves with interference

Organic motion using sine or ripple physics

A calming, almost meditative visual experience

That’s not just animation. That’s simulation.

Now look at what both models actually built.

Both implementations reduce the concept of a ripple to this:

this.radius += this.speed;
this.alpha -= fadeRate;
Enter fullscreen mode Exit fullscreen mode

And then render it using:

ctx.arc(this.x, this.y, this.radius, 0, Math.PI * 2);
Enter fullscreen mode Exit fullscreen mode

That’s it.

No wave equation. No displacement field. No interference. No energy transfer.

Just expanding circles.

Model 1 (GPT-4o): Clean Execution, Shallow Physics

The first model does a respectable job if you judge it purely as a frontend exercise. The structure is clean, the controls are wired correctly, and the animation loop is stable.

ripples = ripples.filter(ripple => ripple.update());
requestAnimationFrame(animate);
Enter fullscreen mode Exit fullscreen mode

There’s discipline here. The lifecycle is well-managed. The UI responds properly. Even small touches like converting hex color into RGB arrays show attention to detail.

But then you notice something subtle that changes everything.

this.intensity = intensity;
Enter fullscreen mode Exit fullscreen mode

It’s defined… and never used.

That one line tells you a lot. The model understood that “intensity” should exist, but didn’t actually connect it to any meaningful behavior—no amplitude scaling, no wave thickness, no energy propagation.

It’s a placeholder for an idea it didn’t fully implement.

The same pattern appears elsewhere. The ripples overlap visually, but they don’t interact. There’s no shared medium, no combined displacement. Each ripple lives in isolation, unaware of others.

And then there’s the rendering:

ctx.clearRect(0, 0, canvas.width, canvas.height);
Enter fullscreen mode Exit fullscreen mode

Every frame wipes the canvas clean. No persistence, no blending, no glow accumulation. The result is visually neat, but sterile. It lacks the richness you expect from something described as “mesmerizing.”

Even the auto-rain system hints at architectural shortcuts:

setTimeout(autoRainEffect, 500);
Enter fullscreen mode Exit fullscreen mode

It works, but it’s detached from the main animation loop. There’s no centralized timing system, which means behavior can drift or stack unpredictably.

So what did Model 1 really build?

A well-structured animation demo that looks like ripples—but doesn’t behave like them.

Model 2 (GPT-4o-mini): Simpler, But Also More Fragile

If Model 1 feels like a clean frontend project, Model 2 feels like a quick prototype pushed out the door.

At a glance, it’s similar. Same expanding circles. Same fade-out logic. Same basic idea.

this.radius += this.speed;
this.alpha -= 0.01;
Enter fullscreen mode Exit fullscreen mode

But the cracks show much earlier.

The rendering approach switches from stroke-based circles to filled ones:

ctx.fillStyle = `rgba(...)`;
ctx.fill();
Enter fullscreen mode Exit fullscreen mode

This creates a very different visual effect—more like blobs than waves. Instead of elegant expanding rings, you get solid discs fading into each other. It’s heavier, less refined, and loses that “water surface” illusion entirely.

Then there’s state management. Instead of filtering the array cleanly, it mutates it during iteration:

if (ripple.alpha <= 0) ripples.splice(index, 1);
Enter fullscreen mode Exit fullscreen mode

This is a classic source of bugs. Removing elements while iterating can skip items or cause inconsistent behavior, especially as the system scales.

The “Auto Rain” feature is where things get particularly telling:

if (autoRainToggle.checked) {
    setInterval(() => { ... }, 1000);
}
Enter fullscreen mode Exit fullscreen mode

This runs once at load. Toggling the checkbox later doesn’t actually control anything. The feature exists in UI, but not in behavior.

Again, we see the same pattern: the idea is present, the implementation is partial.

Even basic constraints like limiting ripple count:

if (ripples.length < maxRipples)
Enter fullscreen mode Exit fullscreen mode

feel more like safeguards against performance issues than part of a thoughtful system design.

And just like Model 1, there is zero attempt at real wave physics. No interference. No medium. No propagation logic beyond “increase radius.”

The Bigger Problem: Both Models Miss the Core Concept

Here’s the uncomfortable truth.

Neither model actually solved the problem.

They both solved a simplified version of it.

What was required:

A system where waves propagate through a medium, interact with each other, and create emergent patterns.

What was built:

Independent objects that draw circles and fade out.

That’s not a small gap. That’s the difference between simulation and animation.

A real ripple system would look more like this:

height[x][y] = wave1[x][y] + wave2[x][y];
Enter fullscreen mode Exit fullscreen mode

You’d have a grid. Each point would store displacement. Waves would travel through that grid, interfere constructively and destructively, and decay over time.

None of that exists here.

And that’s the key insight.

Why This Happens

AI models are incredibly good at pattern matching. They’ve seen ripple effects implemented as expanding circles thousands of times across tutorials, demos, and code snippets.

So when asked to build a ripple system, they default to the most common visual approximation.

It looks right.

It behaves wrong.

And unless you’re specifically looking for physical accuracy, you might not even notice.

So… Which Model Is Better?

If you’re choosing purely on engineering quality, Model 1 is the clear winner.

It has:

Cleaner state management

Better structured animation loop

More consistent UI integration

Fewer logical bugs

Model 2, while functional, feels more fragile and less polished.

But here’s the twist.

Neither model actually delivers what the prompt truly asks for.

So the real takeaway isn’t just “Model 1 wins.”

It’s this:

Both models demonstrate how easy it is to mistake visual correctness for technical correctness.

The Real Lesson

This challenge isn’t about canvas, sliders, or animations.

It’s about understanding what you’re building.

If you think in terms of shapes, you’ll draw circles.
If you think in terms of systems, you’ll simulate waves.

That’s the difference.

And it’s exactly the kind of gap that platforms like Vibe Code Arena expose really well. You don’t just see outputs—you see how different models think about problems.

Sometimes they’re right. Sometimes they’re convincing. And sometimes, like in this case, they’re just approximating something much deeper.

If you want to explore this challenge yourself and see how your thinking compares, try it here:

👉 https://vibecodearena.ai/share/b2ae08e5-d6a7-43ef-9949-64e2eb031a19

Because once you notice the difference between an animation and a system, you can’t unsee it. ;)

Top comments (0)