Skip to main content
Build a Chrome Dino Auto-Player with Digispark ATtiny85
  1. Blog/

Build a Chrome Dino Auto-Player with Digispark ATtiny85

·5 mins· loading · loading ·
Albert David
Author
Albert David
Electronics, IoT, and Embedded Systems

This guide shows how to build a small USB dongle that plays the Chrome Dino game automatically. The device appears as a standard USB keyboard, reads two light sensors on the monitor, and sends jump/duck keys with speed-adaptive timing. No browser extension, no host script, and no mechanical key press hardware.

Digispark ATtiny85 wired to two LM393 LDR sensor boards
Prototype hardware: Digispark ATtiny85 with two LM393 LDR sensor boards (upper and lower obstacle sensing).

Why this approach
#

Common Dino bot methods have tradeoffs:

Approach Typical drawback
JavaScript injection Browser-dependent and easy to detect
Servo or solenoid key presser Bulky, noisy, mechanical wear
MCU + host-side Python script Requires extra software running on the PC

This design is fully self-contained: the host only sees a generic USB HID keyboard.

What is unique in this build
#

Many Dino projects exist, but this implementation combines a few practical differences in one low-cost setup:

  • No host software at runtime: the external MCU enumerates as a standard USB HID keyboard.
  • No mechanical key press actuator: no servo/solenoid pressing the spacebar.
  • Dual-obstacle handling: two vertically stacked LDR sensors support both jump and duck decisions.
  • Speed adaptation in firmware: timing is derived from obstacle envelope width, so behavior stays usable as game speed increases.

Bill of materials
#

  • Digispark ATtiny85 board (~2 to 5 USD)
  • 2x LM393 LDR comparator modules (~1 USD each)
  • Jumper wires (~1 USD)
  • Tape or small clip mount for sensor placement on the monitor

System overview
#

  • Lower sensor tracks ground-level obstacles (cactus zone)
  • Upper sensor tracks bird-height obstacles
  • ATtiny85 classifies obstacle type and sends key press over USB HID
  • Lower sensor pulse envelope width is used to estimate game speed
System overview diagram for Chrome Dino auto-player showing monitor obstacles, dual LM393 sensors, Digispark ATtiny85 and USB HID keyboard output
System overview: obstacle zones on the monitor, dual LM393 sensors, Digispark ATtiny85 firmware logic, and USB HID key output to host.

Wiring
#

LM393 pin Digispark pin
Lower D0 PB2 (D2)
Upper D0 PB0 (D0)
VCC (both) 5V
GND (both) GND
Detailed wiring diagram for Digispark ATtiny85 with upper and lower LM393 LDR sensor outputs connected to PB0 and PB2
Wiring detail: lower LM393 D0 to PB2, upper LM393 D0 to PB0, with shared 5V and GND.

Build and flash
#

Clone and build:

git clone https://github.com/hackboxguy/chrome-dinoplayer.git
cd chrome-dinoplayer
make all

Flash with Micronucleus bootloader:

make upload

If your udev setup does not need sudo:

make upload SUDO=''
Tip

Run make upload first, then plug in the Digispark when prompted.

Build prerequisites (Debian/Ubuntu):

sudo apt install gcc-avr avr-libc libusb-1.0-0-dev pkg-config

Quick start (pre-built firmware already in repo):

make micronucleus
sudo tools/micronucleus/micronucleus --run hex/dino-player-v1.hex

Sensor placement and calibration
#

Chrome Dino gameplay frame used to align sensor position relative to obstacles
Reference frame used to align the upper and lower sensors against bird and cactus obstacle zones.
  1. Open chrome://dino and keep the game in day mode during setup.
  2. Place both sensors at the same horizontal position, slightly ahead of the dino.
  3. Align lower sensor to cactus height.
  4. Align upper sensor to bird flight height.
  5. Adjust LM393 potentiometers so white background is “clear” and dark obstacle is “triggered”.

If your LM393 module polarity is inverted, change OBSTACLE_IS_LOW in firmware.

Key success considerations
#

Two setup points decide whether the bot actually plays well or fails randomly:

  • Vertical sensor alignment: keep both sensors at the same horizontal X position, with lower aligned to cactus zone and upper aligned to bird zone.
  • Distance from dino (tested setup): in my build, the sensor pair works best at about 30 mm to 40 mm ahead of the dino (to the right), which provides enough lead time for action.
  • Per-monitor potentiometer tuning: tune each LM393 threshold based on your monitor brightness/contrast and ambient light. A setting that works on one display or room lighting may fail on another.
Tip

After initial tuning, test at least 20 to 30 obstacle events and fine-adjust the potentiometers in small steps for faster and cleaner trigger transitions.

Detection logic
#

The two vertically stacked sensors produce a simple decision table:

Lower Upper Action
Triggered Triggered Jump (space)
Triggered Clear Jump (space)
Clear Triggered Duck (down arrow)
Clear Clear No action

Rule used in firmware:

  • Upper triggered without lower -> duck
  • Any lower trigger -> jump

Adaptive speed timing (envelope tracking)
#

Game speed increases over time. Fixed delay becomes inaccurate, so firmware tracks the lower-sensor obstacle envelope width and adapts jump timing.

  • Fork gaps inside cactus shapes are merged using a short gap threshold
  • Recent envelope widths are stored in a rolling window
  • Rolling minimum is used as a stable speed reference
  • Jump delay is scaled and clamped between min/max bounds
jump_delay = clamp(rolling_min * scale_factor, MIN_JUMP_DELAY, MAX_JUMP_DELAY)

This keeps timing usable from early game to high score speeds.

Disable day/night inversion in Chrome
#

Night mode flips contrast and can cause false triggers with LDR-based detection. In DevTools Console, run:

Runner.getInstance().invert = function(reset) {};
Note

Run this after each game restart because the function is reset on reload.

Useful tuning constants
#

All values are in src/main.c:

Constant Default Purpose
KEY_HOLD_MS 80 Jump key hold duration
DUCK_HOLD_MS 200 Duck key hold duration
COOLDOWN_MS 400 Minimum time between actions
GAP_THRESHOLD_MS 30 Merge short gaps inside one obstacle envelope
ENVELOPE_HISTORY 5 Rolling window size for speed estimate
MIN_JUMP_DELAY 10 Lower bound for adaptive jump delay
MAX_JUMP_DELAY 150 Upper bound for adaptive jump delay

Troubleshooting
#

  • Device not detected as keyboard: check USB cable/port and Digispark bootloader state.
  • Random jumps or ducks: re-tune LM393 thresholds and re-check sensor alignment.
  • Missed obstacles at high speed: reduce MAX_JUMP_DELAY or tweak scale factor.
  • Build failure: install AVR toolchain and libusb development package.

Where this is useful
#

  • USB gadget experimentation with ATtiny85 and V-USB
  • Low-cost embedded vision-by-threshold prototypes
  • Hardware-only input automation projects

Related