Automating Cookie Clicker with Python: Hooking into an Electron Game

Cookie Clicker is one of those games that starts as a joke and ends with you running a full-blown cookie empire. I wanted to see how far I could push automation — not by cheating with save editors or memory hacks, but by building a bot that plays the game the same way a human does: reading the screen and clicking. Here’s how Auto Clicker for Cookie Clicker works under the hood.

The Core Architecture

The bot uses a two-part architecture: an in-game JavaScript mod that reports UI element positions, and a Python automation layer that reads those positions and injects input.

┌─────────────────────────────────┐     ┌──────────────────────────────┐
│  Cookie Clicker (Electron)      │     │  Python Bot (main.py)        │
│  ┌───────────────────────────┐  │     │                              │
│  │  shimmerBridge mod (.js)  │──┼────→│  DOM loop (80ms poll)        │
│  │  writes JSON coordinates  │  │     │        ↓                     │
│  │  to local file            │  │     │  Priority decision engine    │
│  └───────────────────────────┘  │     │        ↓                     │
└─────────────────────────────────┘     │  pyautogui input injection   │
                                        │  win32gui window management  │
                                        │        ↓                     │
                                        │  PySide6 Qt HUD              │
                                        └──────────────────────────────┘

How the JavaScript Mod Works

The Steam version of Cookie Clicker runs on Electron, which means the game’s UI is all web-based. Since it has a mod system that loads JavaScript from the mods/local/ directory, we can inject our own code directly into the game process.

The mod — called shimmerBridge — does exactly one thing: it reads the positions of every important UI element and writes them to a JSON file on disk. This includes:

Here’s the critical part of the coordinate reporting:

// Inside the Cookie Clicker mod context
function reportPositions() {
    const elements = {
        bigCookie: getRect(Game.bigCookie),
        shimmers: Game.shimmers.map(s => ({
            id: s.id,
            type: s.type,      // 'golden' or 'wrath'
            x: s.l.x, y: s.l.y, w: s.w, h: s.h
        })),
        spells: Game.Objects['Wizard tower']?.minigame?.spellsById
            ?.map(s => ({ id: s.id, name: s.name, ready: s.ready })) || [],
        gardenPlots: getGardenPlotPositions(),
        stockGoods: getStockPositions(),
    };
    // Write to file for Python to read
    Game.WriteSave('shimmerBridge', JSON.stringify(elements));
}

The mod runs inside the game’s own update loop, so positions are always fresh.

The Python Side: Reading Coordinates and Injecting Input

On the Python side, the bot polls that JSON file every 80 milliseconds. This is fast enough to catch golden cookies before they disappear, but slow enough to not hammer the filesystem.

The key challenge is mapping client-relative coordinates (what the mod reports) to absolute screen coordinates (what pyautogui needs). The bot uses win32gui to find the game window and calculates the offset:

import win32gui
import pyautogui

def client_to_screen(client_x, client_y):
    """Convert game-internal coordinates to absolute screen coordinates."""
    hwnd = win32gui.FindWindow(None, "Cookie Clicker")
    rect = win32gui.GetWindowRect(hwnd)
    # Account for window borders and title bar
    border_width = win32api.GetSystemMetrics(win32con.SM_CXSIZEFRAME)
    title_height = win32api.GetSystemMetrics(win32con.SM_CYCAPTION)
    border_adjusted = (
        rect[0] + border_width,
        rect[1] + title_height + border_width,
    )
    return (border_adjusted[0] + client_x, border_adjusted[1] + client_y)

The Priority-Based Decision Loop

The bot doesn’t just click randomly. It runs a strict priority loop that makes real-time decisions:

PRIORITY_ORDER = [
    ("shimmers",      click_shimmers),       # Golden/wrath cookies first
    ("spells",        cast_spells),          # Force the Hand of Fate
    ("stocks",        trade_stocks),         # Buy low, sell high
    ("garden",        manage_garden),        # Plant and harvest
    ("godzamok",      execute_godzamok),     # Sell-click combo
    ("buildings",     buy_building),         # ROI-based purchase
    ("upgrades",      buy_upgrade),          # CPS-boosting upgrades
    ("big_cookie",    click_cookie),         # Last: click the cookie
]

Each tick, the bot checks conditions in this order and executes the first actionable one. Golden cookies are always #1 because they’re time-sensitive — miss one and you lose a potential 777x clicking frenzy.

Stock Market: Hidden State Awareness

The stock market trader is one of the more interesting subsystems. Cookie Clicker’s stock market works on a hidden-state model — each stock has an internal mode (stable, slow_rise, fast_rise, slow_fall, fast_fall, chaotic) that isn’t visible to the player.

The bot tracks price history and uses threshold-based logic to infer the hidden mode:

def evaluate_stock(stock_id, price_history, mode):
    prices = price_history[-10:]  # Last 10 ticks
    delta = prices[-1] - prices[0]
    pct_change = delta / prices[0] if prices[0] else 0

    if mode == "chaotic":
        return "hold"  # Too unpredictable
    if pct_change < -0.15:
        return "buy"   # Deep dip
    if pct_change > 0.25:
        return "sell"  # Strong gain
    return "hold"

The Qt HUD and Controls

The bot ships with a PySide6 Qt dashboard that shows real-time stats and gives you control over every subsystem:

The theme system is centralized — all colors, fonts, and spacing are defined in a single qt_hud/styles/theme.py file, making it easy to customize the look.

Global hotkeys (Ctrl+Alt+F6 through F12) let you toggle features even when the game is focused, which is critical for real-time play.

OBS Overlay for Streamers

The bot includes a standalone OBS overlay server in the obs_overlay/ directory. It receives events from the bot via a local WebSocket connection and renders a transparent browser source showing:

This is particularly useful if you’re streaming the game — your viewers can see exactly what the bot is doing.

Distribution with PyInstaller + Inno Setup

The bot is distributed as a proper Windows installer. The build pipeline uses PyInstaller to freeze the Python code into a standalone executable, then Inno Setup wraps it into a CookieClickerAutoClicker_Setup.exe with:

The entire CI/CD is handled by GitHub Actions — pushing a version tag (v*) triggers the full build and creates a release automatically.

Achievement Safety

One thing I want to emphasize: this bot is achievement-safe. It does not modify save files or hook into game memory. Every action is a simulated mouse click, which is indistinguishable from human input to the game. Cookie Clicker’s developer has stated that auto-clickers are an accepted part of the game’s culture, and the bot won’t trigger any achievement-disabling mechanisms.


Check out the full source and download the installer on GitHub.

Watch me build stuff like this live on Twitch