{"id":15142128,"url":"https://github.com/js13kgames/behind-asteroids-the-dark-side","last_synced_at":"2025-09-29T10:31:31.593Z","repository":{"id":82385017,"uuid":"42742702","full_name":"js13kGames/behind-asteroids-the-dark-side","owner":"js13kGames","description":"Behind Asteroids — The Dark Side - a js13kGames 2015 competition entry by @gre.","archived":false,"fork":true,"pushed_at":"2015-09-16T07:35:28.000Z","size":6982,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":2,"default_branch":"master","last_synced_at":"2024-09-27T09:23:05.603Z","etag":null,"topics":["2015","competition","desktop","game","html5","javascript","js13k","js13kgames","js13kgames2015","mobile","optimization","reversed","touch"],"latest_commit_sha":null,"homepage":"https://js13kgames.com/entries/behind-asteroids-the-dark-side","language":"JavaScript","has_issues":false,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":"gre/behind-asteroids","license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/js13kGames.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2015-09-18T19:20:48.000Z","updated_at":"2022-10-13T02:08:13.000Z","dependencies_parsed_at":"2023-03-07T12:15:59.298Z","dependency_job_id":null,"html_url":"https://github.com/js13kGames/behind-asteroids-the-dark-side","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/js13kGames%2Fbehind-asteroids-the-dark-side","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/js13kGames%2Fbehind-asteroids-the-dark-side/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/js13kGames%2Fbehind-asteroids-the-dark-side/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/js13kGames%2Fbehind-asteroids-the-dark-side/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/js13kGames","download_url":"https://codeload.github.com/js13kGames/behind-asteroids-the-dark-side/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":234611900,"owners_count":18860237,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["2015","competition","desktop","game","html5","javascript","js13k","js13kgames","js13kgames2015","mobile","optimization","reversed","touch"],"created_at":"2024-09-26T09:23:15.484Z","updated_at":"2025-09-29T10:31:29.326Z","avatar_url":"https://github.com/js13kGames.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Behind Asteroids, The Dark Side [![](https://img.shields.io/badge/js13kGames-2015-b12a34.svg)](http://js13kgames.com/entries/behind-asteroids-the-dark-side)\n\n[PLAY](http://js13kgames.com/games/behind-asteroids-the-dark-side/index.html) – [entry](http://js13kgames.com/entries/behind-asteroids-the-dark-side)\n\n## Synopsis\n\nEver wondered what is happening under the hood of an Asteroids Arcade machine?\nI can tell you: A greedy evil 25¢ money maker engine.\n\n## Context\n\nThis is an entry for [js13kGames](http://js13kgames.com/entries/2015), goal is to make a web game in less than 13k zipped of JavaScript.\nTheme was **\"Reversed\"**.\n\n### Inspiration\n\n- The Asteroids original Arcade machine [I've discovered in Brooklyn's Barcade](https://twitter.com/greweb/status/631981745638875137).\n- Youtube videos like [this one](https://www.youtube.com/watch?v=w60sfReTsRA).\n\n**Best quote ever**\n\u003e [\"Not bad for a 35 years old system. 35 years old. Things don't get better than this. They don't get better than this. Look at this picture, you know what, even HD can't be this clear, crystal clear, razor sharp, wonderful vector graphics. Sorry there is nothing like it. *nothing like it*. What a terrific game.\"](https://youtu.be/i-x_gPxqEMw?t=4m14s)\n\n### Special thanks\n\n- [mrspeaker](http://twitter.com/mrspeaker) for his support, testing and English help.\n\n## Game versions\n\nThe game both works for mobile and desktop but the gameplay varies.\nThe desktop version is a [touch typing](https://en.wikipedia.org/wiki/Touch_typing) game\nwhere the mobile version is a simple touch game. If you are not good at typing on keyboards,\njust prefer the mobile version.\n\nOn mobile (especially for iOS Safari), please use **Add to Home screen** for better experience.\n\n---\n\n# The Game\n\nBehind Asteroids is a game about throwing asteroids to people playing \"Asteroids\"\non an arcade machine. Like in Asteroids game, player have 3 extra lifes.\nThe goal is to make the player lose and try to earn as much coins as possible.\nWhen a player lose, another come and put a new coin in the arcade.\n\n\u003cimg src=\"screenshots/explosion.png\" width=\"360\" /\u003e\n\u003cimg src=\"screenshots/dead.png\" width=\"360\" /\u003e\n\u003cimg src=\"screenshots/newplayer.png\" width=\"360\" /\u003e\n\u003cimg src=\"screenshots/playerlose.png\" width=\"360\" /\u003e\n\nThere are different game mechanism involved, they get introduced in first levels\nand get harder and harder to use:\n\n- The Asteroids have an aiming centered in the spaceship that varies the throw velocity\n- The Asteroids aiming rotates (Player \u003e2)\n- The \"RED\" area in the aiming that make you fail the throw (Player \u003e3)\n- The UFO bonus that you get after sending asteroids without failing to throw an asteroid (Player \u003e4)\n\n\u003cimg src=\"screenshots/ufo.png\" width=\"360\" /\u003e\n\u003cimg src=\"screenshots/player24.png\" width=\"360\" /\u003e\n\u003cimg src=\"screenshots/playerlose2.png\" width=\"360\" /\u003e\n\u003cimg src=\"screenshots/explosion.png\" width=\"360\" /\u003e\n\n## Game Over\n\nEverytime the player is reaching 10'000 points, he wins a new extra life,\nYou lose if player reaches 5 lifes.\n\n\u003cimg src=\"screenshots/danger1.png\" width=\"360\" /\u003e\n\u003cimg src=\"screenshots/gameover.png\" width=\"360\" /\u003e\n\n## Continue\n\nGame is saved every time a player entered and can be continued later.\n\n\u003cimg src=\"screenshots/continue.png\" width=\"360\" /\u003e\n\n---\n\n# Tech overview\n\n- Canvas2D for the game primitives drawing\n- [WebGL](src/lib/webgl.js) for post processing effects (7 fragment shaders)\n- [Web Audio API](src/lib/audio.js) + [jsfxr](src/lib/jsfxr.js) ([14 sounds](src/sounds.js))\n- [Asteroid fonts implemented \"by hand\"](src/lib/asteroids.font.js)\n- *... (more to describe later)*\n\n## Making of the post-processing effects pipeline\n\n\u003e Here is an non exhaustive summary of what's going on with the WebGL post-processing effects.\n\nBecause this is a 2D game, a subset of WebGL is used here: we just use 2 triangles that cover the whole surface in order to just focus in writing fragment shaders.\n\n### primitives are down on a simple [2D Canvas](http://www.w3.org/TR/2dcontext/)\nwith classical Canvas 2D code but also\nusing the 3 color channels (RED, GREEN, BLUE) independently to split objects into different classes...\n\n![](screenshots/tech/game.png)\n\n### A [laser shader](src/shaders/laser.frag) draws it to monochrome\n\nIt sums up the 3 color channels. The **BLUE** channel, used for the bullets, gets accentuated in a factor that depends on the screen position. This intends to recreate the various intensity of a vector monitor.\n\nThe result of this shader is also blurred:\n\n![](screenshots/tech/laser.png)\n\n### the [player shader](src/shaders/player.frag) is rendered\nThe player and it environment (that will be reflected in the screen) is procedurally generated in a shader.\n\nThe shader code is a bit crazy right now probably because of all animations, but the drawing is not so complex: this is just about drawing ovale and [squircle]() shapes and also some gradients for the lightning.\n\n![](screenshots/tech/player_raw.png)\n\nWe don't directly use this image in the game, it is visually not very realist, but if we **blur it a lot (and even more on X axis)** to recreate a reflection style, it becomes quite interesting:\n\n![](screenshots/tech/player.png)\n\nThe objective is to find an equilibrium between seeing it a bit in background but not too much. Also note that the hands are moving during a game, this is very subtile to see but it is part of the environment.\n\n### a [Glare shader](src/shaders/glare.frag) effects is added\n\nGlare is obtained by applying a large directional blur.\nIt is only applied on bright objects (basically just bullets).\n\n![](screenshots/tech/glare.png)\n\n\n### Result with some [persistence](src/shaders/persistence.frag)\n\n![](screenshots/tech/persistence.png)\n\nThe final [Game shader](src/shaders/game.frag) combines 5 textures:\n\n```glsl\nuniform sampler2D G; // game\nuniform sampler2D R; // persistence\nuniform sampler2D B; // blur\nuniform sampler2D L; // glare\nuniform sampler2D E; // env (player)\n```\n\nThe blur texture is used as a way to make the glowing effect (multiplying with a blue color).\nThe persistence texture stores the previous blur texture to accumulate motion blur over time.\n\n### Finally, we just put the UI canvas on top of the game canvas\n\n![](screenshots/player24.png)\n\n## Font drawing\n\n![](screenshots/tech/font.png)\n\n[Code is here](src/lib/asteroids.font.js)\n\n## Feedbacks on developing a JS13K game\n\n### Don't start with JS*K tricks\n\nMy first point is about NOT doing any JavaScript tricks to save more bytes until you are at the last days of the competitions and if it happens you actually are \u003e13k (really, 13K zipped is plenty of room for making a game even if not \"bytes optimized\").\n\nSo, you want your game to run first.\nAnd even if you have a first version, you might improve it, so keeping your code readable and maintainable is very important.\n\n### Don't fear beginning with libraries!\n\nUnlike some recommendations I've seen previously about making JS13K games,\nI think you can afford starting with some libraries.\nJust keep in mind to not be too much tied to these libraries so you can eventually remove them.\n\nMy point is, the process of making a game is very long and you want to be\nas productive as possible to prototype and add game features.\n\nIn my game I've used [stack.gl](http://stack.gl) libraries for making the post processing effects, and I only port my code back to raw WebGL when I was really sure it was done.\nI was very productive working on these effects and was not stuck by crazy code.\n\nWhen I was sure of the post-processing pipeline, I've then replaced usage of these libraries by [tiny utility functions](src/lib/webgl.js) specific for my needs.\n\n## Make your game visually debuggable\n\nWhen developing a game, especially an AI, it is important to debug.\nAnd by debug I mean displaying hidden game logic.\nThe problem of `console.log`ging things is it is difficult to picture it with the game for a given instant.\n\nYou want to see vectors and AI decisions to be able to tweak game parameters and improve the game.\n\n[See in this video one display I've used when debugging the AI](https://www.youtube.com/watch?v=1F5XWY4fGaY)\n\n## I have avoided \"OO-style\" to functional style\n\nThere is no \"Objects\" in my game, I've gone away from the classical prototype / OO way of doing games.\n\nWhat I've used is just arrays. This is both a technical choice (going more FP) and a way to save more bytes (a minifier can't rename fields of objects, `[0], [1], ...` are obviously saving bytes especially when zipped).\n\n### Array as data structure\n\nSo instead of objects, I've used array like a tuple.\nFor instance, the `spaceship` state is `[x, y, velx, vely, rot]`\nand `asteroids` state is an array of `[x, y, rot, vel, shape, lvl]`.\n\nAll my game state is in [state.js](src/state.js) and \"tuple types\" are all documented.\n\nTaking this approach, you better have to design your game state first so you don't change this over time (this is the cons of this approach, indexes are not really readable and maintainable).\n\nAlso you should try to make your tuple looking like the same so you can share some code for different types (`x, y` is always the 2 first values in my tuples).\n\n### Embrace Functions\n\nInstead of object methods, I just have a lot of functions.\nFor instance, I have `drawAsteroid(asteroid)`.\n\nSome functions are generic so you can re-use them for different needs.\nI've found a very nice way of implementing **\"behaviors\"** of objects:\n\n```js\n// code from the update loop\neuclidPhysics(spaceship);\nasteroids.forEach(polarPhysics);\nufos.forEach(euclidPhysics);\nbullets.forEach(euclidPhysics);\nparticles.forEach(polarPhysics);\n\nufos.forEach(applyUFOlogic);\nincomingObjects.forEach(applyIncLogic);\n\nparticles.forEach(applyLife);\nloopOutOfBox(spaceship);\nasteroids.forEach(\n  // conditional behavior !!\n  playingSince \u003e 0 \u0026\u0026 !awaitingContinue \u0026\u0026 !gameOver ?\n  destroyOutOfBox : loopOutOfBox);\nufos.forEach(loopOutOfBox);\nbullets.forEach(applyLife);\nbullets.forEach(loopOutOfBox);\n```\n\nAlso, it is easy to pass function as a value:\n```js\n// code from the render loop\nrenderCollection(asteroids, drawAsteroid);\nrenderCollection(ufos, drawUFO);\nrenderCollection(bullets, drawBullet);\nrenderCollection(particles, drawParticle);\n```\n\n## Build system\n\nThe build system is dedicated to JS13K and made with a few simple [scripts](package.json).\nIt is able to copy assets, [concat all files](scripts/concat.sh), [minify the GLSL code](scripts/compileglslfiles.sh), minify the JavaScript, zip the result and give size information.\n\nThings are a bit specific to my need but remain very simple, modular and powerful, you could easily fork it.\n\n### dev\n\n```\nnpm run liveserver\n```\n\n```\nnpm run watch\n```\n\n### prod\n\n```\nnpm run build\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjs13kgames%2Fbehind-asteroids-the-dark-side","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjs13kgames%2Fbehind-asteroids-the-dark-side","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjs13kgames%2Fbehind-asteroids-the-dark-side/lists"}