GET /api/work/galaxy-defense
Galaxy Defense
Archived · · solo
A game I built in high school using drag-and-drop coding blocks. It has a shop, upgrades, a boss, and a teleport ability, all made without the normal tools programmers use to keep code organised.A space shooter built in block code in high school. 790 lines, an economy, and a boss fight, with no arrays or classes available.
- JavaScript
- Code.org Game Lab
- Blockly
- Canvas
- Lines of generated JS
- 790
- Sprites placed by hand
- 17
- Arrays available
- 0

Parameters
Request
curl "https://portfolio.dylansg0318.workers.dev/api/ work/ galaxy-defense"
Response200 OK · 3.3 KB
{ "id": "galaxy-defense", "title": "Galaxy Defense", "status": "archived", "year": 2020, "category": "game", "role": "solo", "summary": "A space shooter built in block code in high school. 790 lines, an economy, and a boss fight, with no arrays or classes available.", "summary_plain": "A game I built in high school using drag-and-drop coding blocks. It has a shop, upgrades, a boss, and a teleport ability, all made without the normal tools programmers use to keep code organised.", "stack": [ "JavaScript", "Code.org Game Lab", "Blockly", "Canvas" ], "metrics": { "lines_of_generated_js": "790", "sprites_placed_by_hand": "17", "arrays_available": "0" }, "links": { "play": "https://portfolio.dylansg0318.workers.dev/play/galaxy-defense" }, "problem": "The assignment was a game in Code.org's Game Lab, Blockly blocks that generate JavaScript, aimed at people who've never programmed. You get global variables, one draw loop, and sprites you place by hand. You don't get arrays you can loop over, classes, collections, or any way to make one thing and stamp out fifty of it. Most projects that come out of it are one mechanic wide, because anything bigger costs linearly more typing.", "unique": "I built a full arcade game inside that ceiling anyway — five attackers, three destructible bases with eight damage states each, a two-hit boss, a bomb pickup, a charge-based teleport and a points shop — by paying for the repetition instead of fighting it. Every alien-versus-projectile pair is written out by hand because there was no loop to put it in. The projectile system cycles three sprite slots with a counter because there was no array to push into. The judgement wasn't any one mechanic, it was deciding which constraints to route around and which to just pay for in volume.", "ai": "None. This predates all of it. It was written in 2020, by hand, in a browser block editor. The only AI involvement is the port on this page: I asked Claude Code to move the original logic onto a plain canvas so it runs here without Code.org's runtime, and reviewed the result against the original line by line. Two things were changed on purpose instead of reproduced, both listed below.", "learned": [ "The shape of your code is set by what your language refuses to give you. Without arrays, every \"for each alien\" became a paragraph of near-identical if-statements, and that's exactly why a loop and a collection feel valuable instead of academic.", "Game state is the hard part, not rendering. The bugs that made it to the end were all state bugs — a base flickering to the wrong damage sprite, a boss whose health reset in the wrong branch — not drawing bugs.", "A shop turns a score into a decision. Prices changed the game from \"don39;t die\" into \"is a bomb worth twenty points right now\", which is the whole difference between a score counter and an economy." ], "cover": { "url": "https://portfolio.dylansg0318.workers.dev/_astro/cover.CrQAW241.png", "width": 1203, "height": 1203, "alt": "Galaxy Defense running: the player's ship at the bottom, two green aliens descending, three green bunkers, and the lives, points, bombs and FTL readouts." }, "page": "https://portfolio.dylansg0318.workers.dev/projects/galaxy-defense" }
Live: this is the answer from this site’s API, not a mock. Send re-runs the request with your parameters; if the answer names a page, it opens.
The problem
The assignment was a game in Code.org's Game Lab, Blockly blocks that generate JavaScript, aimed at people who've never programmed. You get global variables, one draw loop, and sprites you place by hand. You don't get arrays you can loop over, classes, collections, or any way to make one thing and stamp out fifty of it. Most projects that come out of it are one mechanic wide, because anything bigger costs linearly more typing.
What was unique
I built a full arcade game inside that ceiling anyway — five attackers, three destructible bases with eight damage states each, a two-hit boss, a bomb pickup, a charge-based teleport and a points shop — by paying for the repetition instead of fighting it. Every alien-versus-projectile pair is written out by hand because there was no loop to put it in. The projectile system cycles three sprite slots with a counter because there was no array to push into. The judgement wasn't any one mechanic, it was deciding which constraints to route around and which to just pay for in volume.
Where AI fit in
None. This predates all of it. It was written in 2020, by hand, in a browser block editor. The only AI involvement is the port on this page: I asked Claude Code to move the original logic onto a plain canvas so it runs here without Code.org's runtime, and reviewed the result against the original line by line. Two things were changed on purpose instead of reproduced, both listed below.
Parameters
Request
curl "https://portfolio.dylansg0318.workers.dev/api/ work/ galaxy-defense? fields=problem%2Cunique%2Cai"
Response200 OK · 1.5 KB
{ "problem": "The assignment was a game in Code.org's Game Lab, Blockly blocks that generate JavaScript, aimed at people who've never programmed. You get global variables, one draw loop, and sprites you place by hand. You don't get arrays you can loop over, classes, collections, or any way to make one thing and stamp out fifty of it. Most projects that come out of it are one mechanic wide, because anything bigger costs linearly more typing.", "unique": "I built a full arcade game inside that ceiling anyway — five attackers, three destructible bases with eight damage states each, a two-hit boss, a bomb pickup, a charge-based teleport and a points shop — by paying for the repetition instead of fighting it. Every alien-versus-projectile pair is written out by hand because there was no loop to put it in. The projectile system cycles three sprite slots with a counter because there was no array to push into. The judgement wasn't any one mechanic, it was deciding which constraints to route around and which to just pay for in volume.", "ai": "None. This predates all of it. It was written in 2020, by hand, in a browser block editor. The only AI involvement is the port on this page: I asked Claude Code to move the original logic onto a plain canvas so it runs here without Code.org's runtime, and reviewed the result against the original line by line. Two things were changed on purpose instead of reproduced, both listed below.", "page": "https://portfolio.dylansg0318.workers.dev/projects/galaxy-defense" }
Live: this is the answer from this site’s API, not a mock. Send re-runs the request with your parameters; if the answer names a page, it opens.
I made this in my senior year of high school, in Code.org’s Game Lab. You build by dragging blocks and the blocks generate JavaScript. It’s made so someone who’s never written code can finish something in an afternoon. It isn’t made for what I did with it.
What the constraints cost
Game Lab gives you global variables and a draw() loop that runs every frame. No arrays
you can loop over, no classes, no collections. So there’s no way to say “for each alien,
check whether it hit a projectile.” You write it out. Five aliens times three projectiles
is fifteen collision checks, four lines each, identical apart from two names:
}function alienShot() { //Resets each alien when it touches the 3 projectiles if (alien5.isTouching(projectile)) { alien5.x = (randomNumber(10,390)); alien5.y = -15; score = score + 1;}if (alien5.isTouching(projectile2)) { alien5.x = (randomNumber(10,390)); alien5.y = -15; score = score + 1;}if (alien5.isTouching(projectile3)) { alien5.x = (randomNumber(10,390)); alien5.y = -15; score = score + 1;} if (alien4.isTouching(projectile)) { alien4.x = (randomNumber(10,390)); alien4.y = -15; score = score + 1;}if (alien4.isTouching(projectile2)) { alien4.x = (randomNumber(10,390)); alien4.y = -15; score = score + 1;}if (alien4.isTouching(projectile3)) {Same thing with the bases. Three bases, eight damage states each, and the only way to pick one is a comparison per state, twenty-four if-statements for what one array index would say:
function homeHealth() { //Changes the animation of each base when they are hit if (baseHealth == 1) { homeBase.setAnimation("homeBase_8"); } if (baseHealth == 2) { homeBase.setAnimation("homeBase_7"); } if (baseHealth == 3) { homeBase.setAnimation("homeBase_6"); } if (baseHealth == 4) { homeBase.setAnimation("homeBase_5"); } if (baseHealth == 5) { homeBase.setAnimation("homeBase_4"); } if (baseHealth == 6) { homeBase.setAnimation("homeBase_3"); } if (baseHealth == 7) { homeBase.setAnimation("homeBase_2"); } if (baseHealth == 8) { homeBase.setAnimation("homeBase_1");That’s the whole lesson, and I didn’t get it at the time. The repetition isn’t bad style, it’s the only style available. What a loop and a collection buy you is adding a sixth alien without touching fifteen places.
The parts I’m still glad I built
The shop. Hold T and the field freezes into a storefront: bombs for 20 points, a
life for 50, a full repair of all three bases for 200. Prices changed what the score was.
Before the shop it was a number that went up; after it, dying with 190 unspent points was
a real mistake.
The FTL drive. A gauge that fills one percent per frame. At 100 you press Q with a
direction and jump a hundred pixels. An escape with a cooldown, and it made the ship feel
like it had a gear the aliens didn’t.
The boss. At 30 points an angry alien comes in, bounces across the screen, takes two hits and does two damage to a base. One sprite, a velocity flip at each wall and a second health variable, and you’ve got a difficulty curve out of almost nothing.
There’s no pause
This is my favourite thing in the file and I didn’t design it. Game Lab has no way to stop the draw loop, so when I wanted a help card and a shop, the only move was to put the world back where it started on every pass while the key is held:
score = score - 200; } } } //Makes the store sprites visible bombStore.visible = 1; livesStore.visible = 1; homeStore.visible = 1; spaceship.visible = 1; alien.y = -15; alien2.y = -15; alien3.y = -15; alien4.y = -15; alien5.y = -15; //resets alien6 to the top of the screen alien6health = 0; spaceship.x = 200; spaceship.y = 300; bombo.x = randomNumber (10,390); bombo.y = randomNumber (-400,-600); homeBase.visible = 1; homeBase2.visible = 1; homeBase3.visible = 1;It looks frozen. Nothing stopped. Four things follow that I never wrote down anywhere:
The shop is a panic button. Every alien gets teleported to the top of the screen each
frame the menu’s open. About to be hit? Hold T.
The menus are the only respawn. Nothing resets an alien that falls off the bottom,
and the bases only cover part of the width, so any alien that drops through a gap is
gone for good. Play without opening a menu and the game slowly empties out. Hold T for
one frame and all five are back.
The FTL drive charges while you shop. The movement code runs before the store’s reset and doesn’t care if a menu is open.
It costs you your position. The same reset snaps your ship back to the centre and throws the bomb pickup you were three pixels from back above the top of the map.
None of that is in the help text because none of it was intentional. I ported it exactly as written, phase order and all, because a version with a real pause would be a different game.
Porting it here, six years later
The version above is the original logic on a plain canvas, so it runs without Code.org’s runtime. Field size, speeds, spawn ranges, prices, health and the FTL charge rate are all the original’s numbers. Reading my own code back:
A real bug, now fixed. At baseHealth2 === 7 the original sets the damage sprite on
homeBase instead of homeBase2, so the first hit on the middle base visibly changed
the wrong one. It survived because both bases look the same at a glance.
A typo I kept. The teleport spends 98 charge going right and 99 in every other direction. Nobody would notice, and removing it would make the port less true to what I shipped.
A change I made on purpose. The original redraws its starfield at eighteen random positions every frame, so the stars strobe at 60fps. On a school assignment that’s a quirk; on a public page it’s a photosensitivity risk, so the field is generated once and held.
One more. Game Lab runs draw() at 30fps and every speed is per frame. Dropping that
into a 60Hz loop silently doubles the speed of the whole game, so the port steps on a
fixed 30Hz clock and only draws on the display’s schedule.
And a small mercy: p5.play collides on a sprite’s whole image box, transparent margin included, so an alien’s real hitbox was the full 50×50 around a 31×19 drawing. Shots that visibly missed still counted. Collisions here use the visible art.
What I learned
- The shape of your code is set by what your language refuses to give you. Without arrays, every "for each alien" became a paragraph of near-identical if-statements, and that's exactly why a loop and a collection feel valuable instead of academic.
- Game state is the hard part, not rendering. The bugs that made it to the end were all state bugs — a base flickering to the wrong damage sprite, a boss whose health reset in the wrong branch — not drawing bugs.
- A shop turns a score into a decision. Prices changed the game from "don't die" into "is a bomb worth twenty points right now", which is the whole difference between a score counter and an economy.
Parameters
Request
curl "https://portfolio.dylansg0318.workers.dev/api/ work/ galaxy-defense? fields=learned"
Response200 OK · 785 B
{ "learned": [ "The shape of your code is set by what your language refuses to give you. Without arrays, every \"for each alien\" became a paragraph of near-identical if-statements, and that's exactly why a loop and a collection feel valuable instead of academic.", "Game state is the hard part, not rendering. The bugs that made it to the end were all state bugs — a base flickering to the wrong damage sprite, a boss whose health reset in the wrong branch — not drawing bugs.", "A shop turns a score into a decision. Prices changed the game from \"don39;t die\" into \"is a bomb worth twenty points right now\", which is the whole difference between a score counter and an economy." ], "page": "https://portfolio.dylansg0318.workers.dev/projects/galaxy-defense" }
Live: this is the answer from this site’s API, not a mock. Send re-runs the request with your parameters; if the answer names a page, it opens.