Chrome Dino Automation Bot: Building a Real-Time Vision System Under Constraints

From pixel detection to FSM logic — a journey through latency, tradeoffs, and simplification.

Posted by Philip Aron Barlaan on May 28, 2026

Chrome Dino Automation Bot: A Real-Time Game Bot Experiment 🤖

This project is a Python bot that plays the Chrome Dino game automatically using computer vision (basically, it “looks” at the screen 👀), screen capture, and a decision-making system.

At first, this felt like it was going to be a light, fun project — something I could knock out quickly compared to my previous builds.

But it slowly turned into something very different: a long, slightly brutal experiment about speed, reliability, and how hard it is to make something behave correctly in real time.


🔗 Project Access

You can view the full source code here:

GitHub Repository — Initial Dino


The Goal 🎯

On paper, the goal was simple:

Build a bot that can play the Chrome Dino game by detecting obstacles and reacting fast enough to survive.

Coming from projects like my blog website, VoxPDF (PDF-to-audiobook tool), and Watermarkify, I genuinely thought this would be straightforward — just detect cactus, press jump, done.

That assumption aged badly.


Phase 1: The “This Should Be Easy” Version 🧱

The first version was very basic.

It used PyAutoGUI screenshots and simple pixel checking to detect obstacles.

At this point, the logic felt almost too simple:

see obstacle → jump

And for a brief moment, it worked… which made me even more confident that I was close.

That confidence didn’t last long 😅

What I didn’t realize yet was that this was one of those “small but brutal” projects — deceptively simple on the surface, but unforgiving once real-time constraints kick in.


The Hedonic Treadmill Effect 📈

This is where things got interesting mentally.

At first, my goal was just:

“Let’s reach 1,000 score.”

Then it quietly shifted to 3,000.

Then 5,000.

Then 10,000.

At one point I genuinely thought, “If I can just hit 10K once, I’m done — that’s already a win.”

But the moment I reached it, the goal moved again.

Eventually it became 20K… and then 24K.

Even after hitting 24,000, the instinct didn’t stop — there was still this pull to beat it again.

That constant shifting of “enough” was the hedonic treadmill in real time: every milestone felt big for a moment, then immediately became the new baseline.

It made me realize something simple but uncomfortable — progress always rewrites your definition of progress.


Phase 2: Things Get Real ⚙️

As I pushed higher scores, the system started showing cracks.

What looked like a simple bot became a real-time performance problem.

I upgraded to a Finite State Machine (FSM) to structure behavior properly, and switched screen capture to MSS for speed.

This was where things finally started to feel “engineered” instead of hacked together.

And it worked — the bot became fast, responsive, and much more stable.


The Breakthrough (24K Score Run) 🚀

This version started consistently performing well.

It hit 5K, then 10K, and eventually peaked around 24,000 score 🔥

That run felt like a real milestone — not just in score, but in understanding how timing, latency, and system design actually matter in real-time automation.

But stability started becoming the tradeoff.


The Hidden Problem: Real-Time Fragility 👀

The higher I pushed performance, the more I noticed subtle failures.

The system struggled with:

  • Day-night transitions 🌗
  • Visual flickering
  • False obstacle triggers

It wasn’t “broken” — it was just extremely sensitive.

A tiny change in pixels could mean a perfect run… or an instant failure.


Trying to Make It Smarter 🧪

To improve accuracy, I experimented with NumPy-based frame processing and visual preprocessing.

The idea was to make detection more intelligent and less dependent on a single pixel.

It sounded like an upgrade.

In practice, it introduced new instability.

The system became smarter, but less predictable.


Tradeoff: Stability vs Intelligence ⚖️

This was the core tension of the project.

More advanced vision gave better theoretical detection, but worse real-world reliability.

Issues started appearing:

  • Missed obstacles during fast transitions
  • False positives from background noise
  • Inconsistent behavior under speed scaling

Even small timing shifts would cascade into mistakes.


The Ground Scanner Experiment 🌍

I also tried a horizontal ground scanner to improve detection coverage.

It sounded like a good idea — more data should mean better accuracy.

But instead, it created confusion between ground pixels and obstacles.

Jump logic, duck logic, and detection zones started overlapping in messy ways.

I tried patching it with delays and filters, but it never fully stabilized.


The Reset Decision ✂️

Eventually, I scaled everything back.

I removed the NumPy pipeline, preprocessing layers, and ground scanner experiments.

What remained was the simplest and most reliable version:

  • PIL-based screen capture
  • Single-pixel detection
  • FSM decision system

Not the most advanced version — but the one that actually worked consistently.


Final Version 🧩

The final bot is intentionally simple.

It focuses on stability over complexity, and reaction speed over “smart” detection.

And that tradeoff is what made it usable.


What I Learned 📚

  • Simple systems often outperform complex ones in real-time environments
  • Optimization can introduce instability if not carefully controlled
  • Real-time logic is less about correctness and more about timing consistency
  • Progress isn’t linear — it often expands your definition of “good enough”

Final Thoughts 🧠

This project wasn’t just about building a Dino bot.

It was a cycle of:

start simple → get confident → push harder → hit instability → over-engineer → break things → simplify again

And somewhere in that loop, it stopped being just a coding project and became a lesson in restraint, timing, and knowing when “good enough” is actually the best outcome.