I Got Tired of Clicking Cookie Clicker, So I Wrote JavaScript Instead
It started with something incredibly stupid.
- javascript
- automation
- games
It started with something incredibly stupid.
I was playing Cookie Clicker.
If you haven't played it, the premise is exactly what the name suggests: you click a giant cookie, get cookies, buy buildings, get more cookies, and then use those cookies to buy things that make even more cookies.
At some point, the game introduces Golden Cookies. They randomly appear on the screen and give you powerful temporary boosts. One of them, Click Frenzy, gives a ridiculous boost to your clicking power for a few seconds.
There was just one problem.
I wasn't always around to click the Golden Cookie.
And apparently, my solution to this very serious problem was to write JavaScript.
The first step: catch the Golden Cookie
My first thought was to use a normal auto-clicker. But that felt unnecessarily crude.
Cookie Clicker runs in the browser, which means the game itself is written in JavaScript. So instead of looking at the screen and trying to figure out where a Golden Cookie appeared, why not ask the game directly?
That turned out to be much easier.
Cookie Clicker keeps track of its active Golden Cookies internally through an array called Game.shimmers. So I could periodically check that array and look for anything whose type was "golden".
The first version was ridiculously small:
window.goldenBot = setInterval(() => {
Game.shimmers.forEach(shimmer => {
if (shimmer.type === "golden") {
shimmer.pop();
}
});
}, 100);Every 100 milliseconds, the script checks for Golden Cookies.
If one exists, it pops it.
That's it.
I waited for the next Golden Cookie to appear.
It disappeared by itself.
It worked.
That was probably the moment when this stopped being a game and became a programming experiment.
There was one small problem: it eventually clicked Wrath Cookies too. Internally, both use the broad shimmer type "golden"; the wrath flag distinguishes them. So we corrected the detector:
window.goldenBot = setInterval(() => {
Game.shimmers.forEach(shimmer => {
if (shimmer.type === "golden" && shimmer.wrath === 0) shimmer.pop();
});
}, 100);That tiny bug was a useful lesson: the state behind the screen matters more than what an object looks like.
Diagram 1 — How the first bot worked
Golden Cookie spawns → Game.shimmers detects it → shimmer.pop() → reward activated
Then I discovered Click Frenzy
Catching Golden Cookies was useful, but it wasn't the interesting part.
One of the Golden Cookie effects is Click Frenzy. It gives an enormous temporary multiplier to clicking power.
So I thought:
If JavaScript can detect the Golden Cookie, can it also detect the buff?
Yes.
Cookie Clicker exposes its active buffs too, which meant I could check whether Click frenzy was currently active.
That gave me the next upgrade:
if (Game.hasBuff("Click frenzy")) {
Game.ClickCookie();
}Now the script wasn't just catching Golden Cookies.
It could also tell when Click Frenzy was active and start clicking the giant cookie automatically.
So the whole process became:
Golden Cookie appears → automatically click it → Click Frenzy activates → automatically click the big cookie → buff expires → stop clicking.
This was much more satisfying than a generic auto-clicker because the script actually understood what was happening inside the game.
It wasn't blindly clicking a location on the screen.
It was interacting with the game's own state.
Diagram 2 — The Click Frenzy automation
Golden Cookie
│
▼
Detect shimmer
│
▼
Pop the cookie
│
▼
┌──────────────────┐
│ Click Frenzy? │
└────────┬─────────┘
│ YES
▼
Click the Big Cookie
│
▼
Buff expires
│
▼
Stop clickingThen we stopped waiting for Click Frenzy altogether
At this point I had a thought that was probably not going to make the Cookie Clicker developers proud.
If the game lets JavaScript detect Click Frenzy, maybe JavaScript can also trigger Click Frenzy.
It could.
The game exposes a function for adding buffs, so we could repeatedly check whether Click Frenzy was active and activate it whenever it wasn't.
That gave us this:
window.frenzyBot = setInterval(() => {
// Activate Click Frenzy if it isn't already active
if (!Game.hasBuff("Click frenzy")) {
Game.gainBuff("click frenzy", 13, 777);
}
// Click the big cookie while Click Frenzy is active
if (Game.hasBuff("Click frenzy")) {
Game.ClickCookie();
}
}, 10);And suddenly the game had become:
Click Frenzy → click like crazy → Click Frenzy expires → immediately activate another one → repeat.
No more waiting for a Golden Cookie.
No more waiting for the buff.
Just an almost permanent state of:
🍪 CLICK CLICK CLICK CLICK CLICK
To stop it:
clearInterval(window.frenzyBot);We also used a forced Golden Cookie to test the automation without waiting for a real one:
var testCookie = new Game.shimmer("golden");
testCookie.force = "click frenzy";That was particularly fun because it meant I could essentially spawn the exact Golden Cookie effect I wanted and watch the automation react to it.
At this point, Cookie Clicker wasn't really being played anymore.
It was being programmed.
The Sugar Lump rabbit hole
Cookie Clicker also has Sugar Lumps.
Normally, they take hours to mature.
And, honestly, waiting several hours for a tiny piece of digital candy didn't sound particularly exciting anymore.
So I inspected the game's internal state again.
One of the values Cookie Clicker keeps track of is Game.lumpT, which represents the timestamp associated with the current lump's growth.
The game also exposes the amount of time required for the lump to become ripe.
That meant we could essentially tell the game:
"Yeah, this lump has definitely been growing long enough."
After a bit of experimenting, we found a simple approach:
Game.lumpT = Date.now() - Game.lumpRipeAge;
Game.doLumps();That makes the game treat the current lump as having reached its ripe time.
Then we could harvest it.
Because harvesting starts another lump growing, the same idea could be repeated automatically.
The final little lump bot looked like this:
window.lumpBot = setInterval(() => {
// Make the current lump ripe
Game.lumpT = Date.now() - Game.lumpRipeAge;
// Update the game's lump state/display
Game.doLumps();
// Harvest it
Game.clickLump();
}, 100);Suddenly, instead of:
Wait 7–8 hours → harvest one lump
we had:
New lump → instantly ripe → harvest → new lump → repeat.
Diagram 3 — From manual gameplay to automation
COOKIE CLICKER
│
┌─────────────┼─────────────┐
▼ ▼ ▼
Golden Cookies Click Frenzy Sugar Lumps
│ │ │
▼ ▼ ▼
Auto-detect Auto-click Auto-ripe
│ │ │
└─────────────┼─────────────┘
▼
JavaScript Automation
│
▼
🍪 Infinite chaosThen the Grandmapocalypse showed up
Next came the insect-looking Wrinklers around the big cookie. They were exposed through Game.wrinklers, so we could pop the current swarm with:
Game.wrinklers.forEach(w => w.hp = 0);But Wrinklers are strategically useful, which introduced a better idea: a good bot shouldn't simply click everything — it should decide when an action is worthwhile.
Christmas also gave us Reindeer, another shimmer we could detect:
window.reindeerBot = setInterval(() => {
Game.shimmers.forEach(shimmer => {
if (shimmer.type === "reindeer") shimmer.pop();
});
}, 100);The pattern was becoming clear: inspect state → identify mechanic → decide → automate.
The funny part: Cookie Clicker already has a JavaScript Console
At some point, I noticed a building in the game called JavaScript Console.
For a second I thought I had somehow discovered an actual in-game developer console.
Nope.
It's just a building.
But honestly, it might be one of Cookie Clicker's best jokes.
By that point I was already sitting in the browser's real JavaScript console, writing scripts to manipulate the game, while Cookie Clicker was casually selling me a building called JavaScript Console.
It almost felt like the game was acknowledging what I was doing.
"Yeah, you've played long enough. You probably know JavaScript now."
What actually made this fun
The interesting part wasn't really the cheating.
It was discovering how much of the game was accessible directly through JavaScript.
A traditional screen-based bot would have needed to look at pixels, locate a Golden Cookie, move the mouse there, and click it.
Our approach was completely different.
We were talking directly to the game's state.
Instead of saying:
"There is something yellow somewhere around these coordinates."
we could say:
"Does
Game.shimmerscontain a Golden Cookie?"
Instead of:
"The screen looks like Click Frenzy is active."
we could ask:
"Does
Game.hasBuff("Click frenzy")return true?"
And instead of waiting hours for a Sugar Lump, we could inspect its internal timer and tell the game it had reached its ripe point.
That distinction is what made the whole experiment surprisingly fun.
From playing a game to programming one
Cookie Clicker is obviously not designed to be played this way.
And that's kind of the point.
Once you realize that the game is essentially a large JavaScript program running in your browser, the boundary between playing the game and programming the game becomes very thin.
We started with one tiny automation:
"Don't let me miss Golden Cookies."
Then:
"Automatically exploit Click Frenzy."
Then:
"Why wait for Click Frenzy at all?"
Then:
"Why wait several hours for Sugar Lumps?"
And suddenly the browser console had become a little control panel for the game.
What started as a tiny quality-of-life script turned into a miniature exercise in inspecting game state, understanding JavaScript objects, experimenting with internal functions, and automating decisions.
I couldn't tell whether I was playing Cookie Clicker anymore.
Or whether Cookie Clicker had quietly become my JavaScript playground.
Either way...
the cookies are going up. 🍪
Future scope: automate the entire game
Eventually the obvious question became:
Why stop here?
These DevTools snippets could become a proper mini-project: a modular Cookie Clicker automation engine that plays and optimizes most of the game on its own.
We already have Golden Cookie detection, Wrath filtering, Click Frenzy automation, continuous Click Frenzy experimentation, Sugar Lump automation, Wrinkler handling and Reindeer detection.
The next step is to separate the project into three layers:
COOKIE BOT
│
┌─────────────┼─────────────┐
▼ ▼ ▼
Sensors Strategy Actions
│ │ │
Read game state Decide what Click / buy /
buffs, timers, is optimal harvest / cast
prices, etc. │ │
└─────────────┼─────────────┘
▼
Cookie ClickerFrom there, it could automate optimal building/upgrades, buff combos, Wrinklers, seasons, Garden mutations and harvesting, Grimoire spells, Pantheon strategies, Stock Market trading, dragon auras, Sugar Lump spending, achievements and even ascension decisions.
The problem then changes from "How do I automatically click this?" to:
"Given the entire game state, what is the best action the bot can take next?"
There is already prior art: other open-source Cookie Clicker projects automate combinations of purchasing, Golden Cookies, Wrinklers, Garden management, Grimoire, seasonal mechanics and other systems. So the interesting part of our project would be designing our own architecture and strategy engine from the mechanics we've been discovering ourselves.
That turns a joke about missing Golden Cookies into a legitimate mini-project involving state management, event-driven automation, heuristics and optimization.
So the natural next chapter is a GitHub repository.
From goldenBot to an autonomous Cookie Clicker player. 🍪🤖