I Built a JS Game in Photoshop: The Ultimate 2025 Test
Ever thought of building a game inside Photoshop? I did. Discover how I used JavaScript and ExtendScript to push Adobe's giant to its limits in this 2025 challenge.
Alex Carter
Creative technologist and JavaScript enthusiast pushing software to its absolute limits.
What if I told you that your favorite design tool, the undisputed titan of digital imaging, could also be your next game engine? It sounds absurd, right? Like trying to race a cargo ship in a Formula 1 Grand Prix. But the question lodged itself in my brain and wouldn't let go: Could I build a functional JavaScript game entirely inside Adobe Photoshop?
This isn't about finding a better alternative to Unity or Godot. This is about pushing a piece of software to its absolute, screaming-at-the-heavens limit. It’s a stress test, a creative coding challenge, and a deep dive into the dusty, often-overlooked corners of Photoshop's scripting engine. I decided to call this experiment 'The Ultimate 2025 Test' because it's about forcing a legacy environment to handle modern development concepts, just to see what would break first: the software, or my sanity.
Why on Earth Would You Do This?
Let's get the obvious question out of the way. The primary reason is curiosity. As developers and creators, we thrive on asking "what if?". This project is the embodiment of that question. It’s about understanding a tool not just for what it's designed for, but for the full scope of what it's capable of. Other reasons include:
- Deep Learning: You never truly understand a system's limitations until you intentionally try to break them. This was a masterclass in Photoshop's document object model (DOM).
- Creative Coding: It blurs the line between design and development, using code as a creative medium within a visual canvas.
- The Challenge: Let's be honest, the absurdity is part of the fun. It’s a story worth telling.
The Tech Stack: Photoshop, ExtendScript, and a Dash of Insanity
Our "stack" is unconventional, to say the least. The entire environment is a single application.
- The Renderer & Canvas: Adobe Photoshop CC 2025. Every pixel, layer, and guide is part of our game world.
- The Game Engine & Logic: JavaScript, but not the JS you know and love. We're using Adobe ExtendScript.
ExtendScript is Adobe's scripting language, which is based on the ancient ECMA-262 3rd edition standard (ES3). Think JavaScript from 1999. This was the single biggest hurdle. To put it in perspective, here's what you're giving up compared to modern JavaScript (ES6+):
Feature | Modern JavaScript (ES6+) | ExtendScript (ES3) |
---|---|---|
Variable Declaration | let , const |
Only var (function-scoped) |
Functions | Arrow Functions (=> ) |
Traditional function() {} only |
Asynchronous Code | Promises , async/await |
None. Everything is blocking. |
Arrays | .forEach() , .map() , .filter() |
Old-school for loops. Some polyfills work. |
Modules | import / export |
#include preprocessor directive |
The Game Concept: "Pixel Corruptor"
To make this feasible, the game had to be simple and lean into Photoshop's strengths (and weaknesses). I landed on a concept called "Pixel Corruptor."
The Goal: A single, pure white pixel (the "player") appears on a 256x256 pixel black canvas. Every second, a new red "corruptor" pixel appears at a random location. The player must "move" their white pixel onto the red pixel to "cleanse" it and score a point. If 10 corruptor pixels exist on the canvas at once, it's game over. The challenge? You don't have a keyboard. You move by clicking where you want to go, then re-running the script to process the move.
The Core Game Loop: A JavaScript Time Machine
In a normal game engine, you have a real-time loop that runs dozens of times per second. In Photoshop, there's no such thing. The entire game state is processed in a single, monolithic script execution. The "loop" is manual: the player running the script is the loop.
Here’s a simplified version of the logic in ExtendScript:
// Simplified ExtendScript Game Loop
#target photoshop
var doc = app.activeDocument;
var scoreLayer = doc.layers.getByName("Score");
var playerLayer = doc.layers.getByName("Player");
// 1. UPDATE GAME STATE
// ========================
// Check for collision (is player pixel on a corruptor pixel?)
// This is a very slow operation!
var isCollision = checkPixelCollision();
if (isCollision) {
updateScore();
removeCorruptorPixel();
}
// Check for game over condition
var corruptorCount = getCorruptorCount();
if (corruptorCount >= 10) {
alert("Game Over! Your score: " + getScore());
// end script
} else {
// 2. RENDER NEW STATE
// =======================
spawnNewCorruptorPixel();
}
// 3. AWAIT NEXT "FRAME" (user running script again)
// =======================
alert("Frame complete. Click to move, then run script again.");
// --- Helper Functions Below ---
function checkPixelCollision() { /* ... complex pixel color checking logic ... */ }
function updateScore() { /* ... logic to update text layer ... */ }
// etc...
Every action—moving, cleansing, spawning—happens in one go. The script runs, updates the canvas, and then stops, waiting for the user to trigger the next "frame.". It's less of a game and more of a turn-based strategy against yourself.
Challenges & Breakthroughs
This project was 90% challenges, 10% breakthroughs. Here are the big ones:
Challenge: The Unbearable Slowness of Being
Reading and writing individual pixel data in Photoshop via script is agonizingly slow. A simple loop to check 10 pixel locations could take several seconds. The breakthrough was to stop thinking in pixels and start thinking in layers. Instead of drawing pixels, I created a 1x1 pixel layer for the player and each "corruptor." Moving the player was now a matter of changing a layer's coordinates (`layer.translate(x, y)`), which is dramatically faster.
Challenge: How Do You 'Click' in a Script?
ExtendScript has no native event listeners for mouse clicks within the document. How could the player tell the script where to move? The solution was hacky but effective: The Color Sampler tool. The player would use the Color Sampler tool to place a point where they wanted to move. The script would then read the coordinates of `doc.colorSamplers[0]` as the destination for the player layer on the next run. It's clunky, but it works!
Breakthrough: Abusing Metadata for Fun and Profit
How do you store the score or game state between script runs? When the script ends, all its variables die. The answer was Photoshop's file metadata. I could write the current score directly into the document's XMP metadata (`doc.xmpMetadata.rawData`). On the next run, the script would first read this data to restore the game to its previous state before executing the new turn's logic.
The Verdict: Is Photoshop a Viable Game Engine?
In a word: No. And that's okay. It was never meant to be. But the experiment was a resounding success in what it taught me. Here’s a final breakdown:
Pros | Cons |
---|---|
Unmatched visual and pixel-level control. | Catastrophic performance for real-time tasks. |
Seamless integration with design assets. | Ancient, feature-poor JavaScript (ES3). |
Powerful API for document/layer manipulation. | No native input, audio, or physics systems. |
Forces creative problem-solving. | The workflow is painfully slow and non-interactive. |
Final Thoughts & The Code
Building a game in Photoshop is a ridiculous, frustrating, and deeply rewarding journey. It forces you to throw out modern conventions and solve problems with the limited tools at your disposal. It's a testament to the power of asking "what if?" and a fantastic way to appreciate the incredible engineering that goes into actual game engines.
While you won't be shipping the next indie hit from a `.PSD` file, you will gain an unparalleled understanding of the software you use every day. You'll learn to think outside the layer panel and see code as just another tool in your creative arsenal.
If you're brave enough to try this yourself, I've open-sourced the (admittedly messy) code. Go break something. You might just learn a thing or two.
Check out the full ExtendScript code for 'Pixel Corruptor' on GitHub.