Skip to main content

Command Palette

Search for a command to run...

I Built a "Pattern Memory" game ft. Vibe Code Arena

Updated
4 min read

Everyone thinks this game is about remembering colors.

Red → Blue → Green → Yellow.

Repeat it. Add one more. Repeat again.

Classic.

But the moment you try to build it… something strange happens.

You realize the hardest part isn’t the sequence.

It’s control.

The real problem shows up immediately

You light up the first pad.

User clicks.

Cool.

You add another step. Play the sequence.

User clicks again… during playback.

Now what?

Did they:

  • interrupt the sequence?

  • queue input?

  • break the game?

Most implementations don’t answer this clearly.

They just… hope timing works out.

This is not a memory game

It’s a phase management problem

There are only two real states:

"playback"
"user"

That’s it.

But everything depends on respecting that boundary.

Because the rule is simple:

If the system is speaking, the user must be silent.

Where most versions quietly fail

They don’t enforce the boundary.

They rely on timing instead.

Something like:

setTimeout(playNext, 500);

And assume the user won’t click fast enough to interfere.

That’s not a system.

That’s optimism.

The moment it becomes stable

You introduce a gate.

Not visually.

Logically.

if (phase !== "user") return;

That single line does more than prevent bugs.

It defines authority.

Now:

  • during playback → input is ignored

  • during input → system is silent

No overlap.

No ambiguity.

The sequence isn’t the hard part

People overthink this:

sequence.push(randomPad());

That’s easy.

The difficulty is not storing the sequence.

It’s replaying it correctly over time

Why async/await changes everything

Without it, you end up nesting timeouts:

setTimeout(() => {
  lightPad(a);
  setTimeout(() => {
    lightPad(b);
  }, 400);
}, 400);

This works.

Until you need:

  • speed changes

  • interruption handling

  • clean resets

Then it becomes unreadable fast.

Now compare that to:

async function playSequence() {
  phase = "playback";

  for (const step of sequence) {
    await light(step);
  }

  phase = "user";
}

This isn’t just cleaner.

It mirrors how the game actually behaves.

Sequential. Controlled. Interruptible.

The pad lighting is not visual

It’s temporal feedback

A pad lighting up is doing three things at once:

  • showing sequence

  • playing sound

  • blocking input

That’s a lot of responsibility for one interaction.

Even something tiny like:

osc.frequency.value = freqMap[color];

isn’t just sound.

It’s identity.

Each color becomes recognizable through audio.

Which reduces cognitive load for the player.

Speed increase is where timing bugs surface

At low levels, everything feels fine.

Delays are forgiving.

But as speed increases:

  • overlaps happen

  • inputs get misread

  • playback feels rushed

Because most systems tie speed to delay:

delay = 500 - level * 20;

Looks fine.

Until delay becomes smaller than animation duration.

Now your system contradicts itself.

The version that holds up

Doesn’t just reduce delay.

It keeps relationships consistent.

Playback time, animation time, and input windows stay aligned.

Not equal.

Aligned.

The high score feature seems unrelated

It isn’t

localStorage.setItem("highScore", score);

This line introduces persistence.

Which means your system now has:

  • runtime state

  • stored state

And they must agree.

If your game resets incorrectly…

Your high score becomes meaningless.

What shows up in Vibe Code Arena

This challenge creates a very specific kind of failure.

Not visual.

Behavioral.

One model builds a working loop:

  • sequence plays

  • user clicks

  • game progresses

But input leaks into playback.

Timing conflicts appear at higher levels.

It works… until it doesn’t.

Another model enforces strict phases:

  • clear separation of control

  • async sequence playback

  • input only accepted at the right time

Now the system feels tight.

Not because it’s faster.

Because it’s consistent

The human version tends to do one thing right

It treats the game like a conversation.

Not a loop.

System speaks → user listens User responds → system evaluates

Never both at the same time.

The part you don’t expect

After building this, you stop seeing:

“a color memory game”

And start seeing:

“a synchronized system with exclusive control phases”

And that pattern shows up everywhere:

  • multiplayer games

  • collaborative tools

  • real-time editors

Anywhere timing and control matter.

Try this before you move on

Don’t just build it.

Break it.

  • Click aggressively during playback

  • Speed up the sequence beyond comfort

  • Reset mid-animation

Watch where your system cracks.

That’s the real challenge.

If you want to see how different approaches handle that tension between control and chaos, this is the exact arena where it plays out:

👉 https://vibecodearena.ai/share/739ed12f-cbd5-409d-b647-ca4d7b0d764c

Not just to complete it.

But to feel when the system finally locks in.