Some 2D Space Shooter


I realized that with PacMan being copyright, it’s a bit tough to share to showcase the potential for making games in SuperCollider.

Here’s another crack at making some 2D games I started today. This one is the beginning of a top down, 2D outer space shooter that has no enemies or goals yet. Note - I have absolutely no idea what the unicode for space is on Linux or Windows. 20 was my best guess for Windows, but this works*** on a Mac.

Enjoy and feel free to reuse the code for whatever.

***with some obvious bugs in how the laser beams behave :joy:

s = Server.local;
var title = "SuperSpaceCollider";
var winW = 480, winH = 640;
var win =, Rect(300,100,winW,winH), false)

var keycodes =
	//<-, ->, ↓, ↑
	\osx, {[123,124,125,126,49]},
	\linux, {[65361,65363,65364,65362]},
	\windows, {[37,39,40,38,20]}
var numLasers = 0;

var movementSpeed = 3;

var playerDir = Set(1);

var firing = false;

brown = Color.new255(216, 145, 85),
pink = Color.new255(252, 180, 255),
red = Color.new255(252, 0, 0),
salmon = Color.new255(252, 180, 170),
orange = Color.new255(252, 180, 85),
yellow = Color.new255(252, 252, 0),
green = Color.new255(0, 252, 0),
teal = Color.new255(72, 180, 170),
cyan = Color.new255(0, 252, 255),
blue = Color.new255(72, 180, 255),
indigo = Color.new255(36, 36, 255),
white = Color.white,
black =,
clear = Color.clear;

var spriteH=35, spriteW=35;

var playerX=winW/2-spriteW, playerY=winH;
		var lasers = 0, laserX = playerX, laserY = playerY, shipScale=1;

var board = UserView(win, win.view.bounds)

var playerSprite = UserView(board, win.view.bounds)
		var cockpitH = spriteH/3, cockpitW = spriteW/7;
		var numLines = 4;
		arg item;
	{item == (\left)} {playerX=playerX-movementSpeed}
	{item == (\right)} {playerX=playerX+movementSpeed}
	{item == (\down)} {playerY=playerY+movementSpeed}
	{item == (\up)} {playerY=playerY-movementSpeed};
	playerX=playerX.clip(spriteW.neg/2, win.view.bounds.width-(3/2*spriteW));
	Pen.translate(playerX+(spriteW/2), playerY);

		Pen.moveTo(spriteW/2 @ 0).scale(shipScale, 1);
		arg i;
			var ratio = (1/2);
			.moveTo(spriteW/2 @ (7*spriteH/8))
			.lineTo(i*spriteW @ spriteH)
			.lineTo(i*spriteW @ (15*spriteH/16))
			.quadCurveTo(spriteW/2 @ 0, spriteW/2 @ (spriteH*ratio))
		arg i;
			.line(spriteW/numLines*(i+0.5) @ spriteH, spriteW/numLines*(i+0.5) @ (7*spriteH/10))
		arg i;
			.line(i*spriteW @ spriteH, i*spriteW @ (6*spriteH/10))
		arg i;
		.moveTo((spriteW/2) @ 0)
		.quadCurveTo(spriteW/2 @ (spriteH), spriteW/4+(i*spriteW/2) @ (spriteH/2))
		Pen.addOval(Rect(spriteW-cockpitW/2, (4*spriteH/10), cockpitW, cockpitH))
		arg i;
		Pen.color_([cyan,blue,indigo, white].choose)
		.line(17.5 @ (7*spriteH/8), 15.rrand(20) @ ((1*spriteH/2).rand + (7*spriteH/8)))
	var laserView = UserView(playerSprite, win.view.bounds)
		{firing == true}
			arg item, i;
			laserY = laserY-4;
			Pen.line(item@laserY, item@(laserY+5)).width_(3).color_(red).stroke});

	var pow = {
		var num = 3.rrand(7);
			arg i;*,0.5,0.2), iphase:0.0, width:0.1.rrand(0.9)*, (pi/2).rand),,0.5,curve:-4), doneAction:2))
			playerX.linlin(0, winW, -1.0, 1.0))};

		arg view, char, mod, uni, keycode;
	{keycode == keycodes[0]} {playerDir.add(\left);}
		{keycode == keycodes[1]} {playerDir.add(\right);}
	{keycode == keycodes[2]} {playerDir.add(\down);}
	{keycode == keycodes[3]} {playerDir.add(\up);}
		{keycode == keycodes[4]} {; laserX = [playerX+(0.5*spriteW), playerX+(1.5*spriteW)]; laserY = playerY+(spriteH/2); firing = true; numLasers=numLasers+1}
		{uni == 27} {win.close; win.refresh};
		arg view, char, mod, uni, keycode;
	{keycode == keycodes[0]} {playerDir.remove(\left);}
	{keycode == keycodes[1]} {playerDir.remove(\right);}
	{keycode == keycodes[2]} {playerDir.remove(\down);}
	{keycode == keycodes[3]} {playerDir.remove(\up);}
	{keycode == keycodes[4]} {numLasers=numLasers}

This is great. The level of detail for the sprite and the laser sounds is much appreciated. I can’t wait to play the whole thing when it is “done”.


Just tried in Linux and Windows – space is 32 in both. (“20” sounds like hexadecimal – 0x20 = 32.)

It’s slick!

Also try making a SynthDef for “pow” instead of playing a function (better for timing, and efficiency).


Question James, how can I get playerY to set the value of from a SynthDef? Would I just call Synth.set from drawFunc_({})? And I was also really unsure about iterating with rrand in a SynthDef… This isn’t possible right? I agree about the SynthDef though, I really should just pick a number and call it a day probably.

Thanks in advance,

EDIT: Never mind, I got it :slight_smile:

Hi Mjsyts,

this is great and I love seeing someone interested in making games! Me too.

I’ve been writing a game engine in Supercollider for the last 2 years and I’m getting close to putting something out. Maybe this is something that might help you.

neil : )


Absolutely! Are you on GitHub? I’ve found this to be a lot of fun. I was really uncomfortable with GUI when I started and I was getting tired of making excuses about it.

Yes the project is on GIT hub but its private at the moment.
Let me check I haven’t got any copyrighted assets in it before I go public.

1 Like

The first playable version is complete! Updates will likely be forthcoming to make it more difficult and fine tune a few game mechanics, but I am thrilled at how well it runs so far.


Amazing work!! Thanks for sharing!