I grew up in the 80s with the Z80, CPC464, and the Amiga 500 as companions. I enjoyed then coding in assembler, basic, pascal and C.
Now I live in Paris, working in IT but I don't often get the opportunity to program. I mainly use awk and python for some basic scripting.
I like PICO-8 - seems like a travel in time, coding only for the pleasure, quickly share and show off, learn from others
Fireworks simulation in less 200 tokens !
x starts a new game
There are some known issues :
- the ball is a 2x2 square but only the upper left pixel is used to detect collisions with the bricks
- hitting x the next seconds after losing a ball resets the game
- bounces on bricks doesn't work as expected in rare cases
- in the original game, hitting particular colors speeds up the ball - not here
Enjoy
I initially just wanted to display a large text for a title screen... and I ended with this.
use:
left and right to control the scrolling speed (two ways)
up and down to control the wave frequency
call scan_text() at the beginning of your program : it clears the screen twice
you can add your own effects very easily into put_sintext() - the existing five ones should help
For your games, you could have to manage a score greater than 32768 (I like games were I can score millions), and often, you want the score displayed in a fixed length, prepended with zeros (00072635).
But managing this can consume lot of tokens.
I came accros a solution I'm quite proud (I'm still learning Lua...) and wanted to share.
In Lua, if you do : "the score is "..score , score is converted into a string and then concatenated to the first one.
So to convert a number into a string, you just have to write : ''"..score
I wrote a recursive function that takes a string and a number (the length) and returns a string padded with zeros at its beginning to reach the desired length :
function pad(string,length) if (#string==length) return string return "0"..pad(string, length-1) end score=723 print(pad(""..score,7)) -- will display 0000723 |
Elegant and thrifty, isn't it ?
Then, you can use two variables, score2 will store the 10 thousands, score1 anything under 10000
each time your player scores something, you add it to score1 and you test if it's above 9999 :
if score1>9999 then score1-=9999 score2+=1 end |
Be sure the max score won't never be above 327689999 ... or manage it with a score3 variable
Here is the concept in action - score2 is displayed in orange, score1 in red:
Today I was watching Roland Garros on the TV when I figured I would waste less my time playing with PICO-8 and so I did ... another pong.

The code is quite simple and should help beginners starting with PICO-8 programming
It supports different bouncing angles - I store them in a table for each pixel of the paddle (8 pixels high) :
angles={-1.2,-0.8,-0.5,-0.2,0.2,0.5,0.8,1.2} ... b.dy=angles[flr(b.y-p1y)+1] |
At first, player2 (p2) was always following the ball and was unbeatable... so I modified it to only follow the ball when it is in the right half of the screen - I found this solution giving a good difficulty level - you can raise it if you increase the area where it "sees" the ball :
-- p2 follows the ball, only if the ball is on the right side of the screen if (b.x>64 and p2y+4>b.y ) p2y-=1 if (b.x>64 and p2y+4<b.y ) p2y+=1 |
Enjoy !
I was playing with the sprite editor, and found my creatures nice enough to make a game.
I'm still focused on my lode runner project, so I wanted to keep it short and simple.
this won't be the game of the year, and to give it a use, I did my best to document the code so eventually it can help beginners.

the game is simple (boring ?) - fire and hit a max. of alien before you die
but there's room to improve it :
- increase the difficulty by adding new aliens every 10 points
- move the aliens in direction of the player when it is at a certain range
...
function new_alien() a={} -- a new alien is allocated a.x=rnd(120) -- aliens appear on the top 1/3 of the screen a.y=rnd(40) a.spr=flr(rnd(6))+2 -- and can be of any of the 6 alien sprites a.dx=rnd(4)-2 -- direction and speed is random a.dy=rnd(5)-2 -- but more often going down than up add(aliens,a) -- the new alien is added to our list of aliens end function _init() -- called once at the start of the programm x=60 -- player's fighter initial position y=100 hold_fire=0 -- used prevent continuous fire score=0 best=0 -- the high score bullets={} -- list of active bullets aliens={} -- the list of aliens for i=1,20 do new_alien() end -- creates 20 aliens cls() -- clear the screen print("saccharine",44,38,7) -- and displays a title screen print("PRESENTS",48,48,6) print("until death",43,58,8) print("x start",50,98,6) while not btn(5) do end -- waits for x key to be pressed end function _draw() cls() -- clear the screen spr(1,x,y) -- displays player's fighter sprite for a in all(aliens) do -- for each alien, a.x+=a.dx -- calculate its new position a.y+=a.dy if a.x>0 and a.y>0 and a.x<127 and a.y<127 then -- if alien is still inside the screen spr(a.spr,a.x,a.y) -- displays it if a.x+2>=x and a.x+2<=x+7 and a.y+2>=y and a.y+2<=y+8 then -- alien collision with fighter del(aliens,a) -- remove the alien from the list of aliens (we won't display it anymore) new_alien() -- and replace it by a new one so that the number of aliens is always the same sfx(2) -- louder crash sound than for the aliens x=60 -- respwan y=100 bullets={} -- clears all active bullets score=0 -- start over end else del(aliens,a) -- alien leaved the screen, delete it new_alien() -- and replace it by a new one end end for b in all(bullets) do -- parsing each active bullet b.y-=3 -- move it up if b.y<0 then -- the bullet exits the top of the screen del(bullets,b) -- we remove it from the list of active bullets else if pget(b.x,b.y)!=0 then -- if it hits something del(bullets,b) -- we delete it sfx(1) -- alien explosion score+=1 for a in all(aliens) do -- search which alien we killed if a.x<=b.x and a.x+5>=b.x and a.y<=b.y and a.y+4>=b.y then del(aliens,a) -- delete it new_alien() -- and replace it by a new one end end else pset(b.x,b.y,7) -- displays the bullet pset(b.x,b.y+1,8) -- and a red dot behind it end end end if (score>best) best=score -- displays updated high score, and current score print(best,0,0,6) print(score,0,8,6) end function _update() if (btn(0) and x>0 ) x-=2 -- going left if (btn(1) and x<120) x+=2 -- right if (btn(2) and y>50 ) y-=2 -- up if (btn(3) and y<120) y+=2 -- down hold_fire+=1 -- increases the timer 30 times per second if (btn(4) or btn(5)) and hold_fire>8 then -- allowed to fire again sfx(0) add(bullets,{x=x+1,y=y}) -- left gun add(bullets,{x=x+5,y=y}) -- right gun hold_fire=0 -- the timer is reset to zero, so we have end -- to wait it reaches 9 to fire again end |
read the code, make it yours, improve the game, show off your changes and explain how you did
... or beat the score
I discovered PICO-8 four days ago after reading a news in The Verge about the upcoming PocketCHIP. I purchased a license (the voxatron bundle) to see what it was about and since I can't stop playing with and the numerous games / demos.
I looked at some code, read some posts on the forum and thought it could be even more funny to code.
So I decided to start with a Lode Runner remake as my first program.
It might be a bit ambitious as it's been almost 15 years I haven't coded anything serious but I have some time and I'm decided to learn.
It's still at the very early stages, I managed to fit the first level into to 128x128 and draw the sprites set :
and I can display them together :
But that's it for now...
Part 2
Today, after a long weekend, I'm back to PICO-8 devellopement.
Lode Runner runs !

I had some trouble with transparency - looks like sprite 0 has to be blank (?) - not sure how flip() really works ... there must be a better way to refresh the screen than :
function _draw() rectfill (0,0,127,119,0) map(0,0,0,0,16,16) spr(runner.sprite,runner.x,runner.y) end |
Moving my main sprite has been a source of satisfaction. I still need to sync the speed but it already looks nice to me.
The most difficult has been with the colision detection. It works but I'm not happy with my code.
the actor's states are far more complex than I initially thought
I should have think a bit more before diving into the code and now I think it would be better to restart from scracth before going too far.
That's the way you learn ;-)