Another autumn, another PICO-1k Jam. I love this jam so much ~ the 1024 byte .p8.rom size limit hits a sweet spot where every little design and code choice carries so much weight, and you end up with so many overlapping meanings, but still with a sense of broad possibility. For me the jam is like a tiny hyper PICO-8 inside PICO-8 that always carries me along to new places. Thanks @Liquidream for hosting and greetings to everyone taking part! You can find entries on the jam's itch page or on the BBS via the pico1k tag. And if you'd like to join, there is still just under a week left of September!
Instructions
- CTRL-R to restart (or ENTER to select reset from the pause menu)
- [O] (Z/C) to jump and use jetpack
- [X] to dig (LEFT and RIGHT while standing only!)
- [X] + [DOWN] to drop an explosive (falls through topsoil)
- Collect power gems for score (and +3 pixels of energy!)
- Watch out for: falling sand, lava, and depleted energy
- Fly up to warp to a new planet at any time.
- There is no win state; perish with the glory of many gems collected.
It's up to you to decide if each gem is worth trying to collect, if each nook of a cave is worth exploring, and when it is worth flying up to warp to a new planet. Using the jetpack and digging use energy at the same rate (around 1 pixel of energy per second), and dropping explosives costs half a pixel of energy each. Running out of energy means instant doom and is a dishonorable way to die, comparable to falling into a tiny pool of lava. Good luck!
How it Works
// note: click "Code ▽" under the playable cartridge above to view commented src code!
This jam I wanted to see how far I could get with a procedurally generated world. In the past I've experimented with hand-designed coarse structures that are embellished with hard-coded details (Squiddy) and also using P8SCII for finer control over single tiles (Blue and Lefty). The problem with forgoing hand-designed elements with such a small code size is that it is very easy to end up with 10000 bowls of oatmeal. You really want generated structures to have a visual hierarchy, to play different roles at different scales, and for those structures to meaningfully interact with the game rules.
World Generation
The key discovery I made for this to happen in 1k was that the generation and world simulation could be closely intertwined. All of the world state (apart from the player) is encoded as pixel data in the spritesheet; things like falling explosives and blast sparkles along with the more obvious falling sand and lava, are all driven by tile-based rules (a variety of Cellular Automata). So the world generation is a combination of two passes: an initial state that the player never actually sees, and the result of running the core game rules (sand piles up, lava-falls and rivers form).
The world is generated by starting with horizontal bands of the desired tile indexes (with a layer of zero for caves), and then distorting them by drawing many circles using the colour at the center of the circle before it is drawn. Finally, pockets of sand and lava are added along with the collectable power gems. The sand is index 7 (white) and lava is index 3 (green):
The size of circles drawn, and the number of circles can be used to control the general lumpiness and scale of distortion. Both of these factors are derived from the planet seed sd:
-- ye olde tweetcart decay effect for i=1,sd do x,y=rnd(128),33+rnd(88) c(x,y,y/(18+sd%18),pget(x,y)) end |
After the planet finishes generating you can see the lava and sand (near the player x position) forming piles and rivers. The indexes of the three special tiles (1 gems, 3 lava, 7 sand) are chosen to correspond with the desired range of pocket sizes, and so that pass is simply:
for x=2,222 do -- range chosen to compress well
k=rnd{1,3,7} -- gem,lava,sand
circfill(x,120-rnd(7)^2,rnd(k),k) -- rnd(7)^2 to spawn mostly deeper
end |
The first row of sprites is used as graphics, and the rest of it as 4-bit tile indexes into that row (the map memory is not used at all). This allows using gfx operations like circfill() to draw the world, P8SCII to generate the sprite gfx, and then memcpy()ing from 0x6000 (display) to 0x0 (spritesheet) once each time a planet is entered. This means that if flying up to new planets is disabled, you can jetpack up and fly around the spritesheet:
I was unable to hide the few frames of world generation drawn to the screen without spending many compressed bytes, so leaned into it and treated it as an inter-planet warp effect. It is still quite jarring, but it helped to design the starting sound to complement that, and to throw the player into the world with a large dx,dy (starting values that also compressed better, as it turned out).
Sprite Generation
The gfx is drawn using P8SCII print statements, with the starting warp sound tacked on to the end with the \a control character:
?"c0-b a◆●\#89-_\-hb\#02o"..sub(sd,1,3)..",'d\#3o!"..sub(sd,-2).."ˇ\#42-o.`\#f9▒\#6d◝◜◝░a-o\#0░░\0" ?"\|je|\-f|\-h,\|j|js0hg9\*2:0000000303030000gc\*2:3c3e3e3e3c3c0000gdod6b - -\ax6[cegb]<cegb]<cegb" |
The display colour for 10 (yellow) is updated every frame to a rainbow cycle, and colours 3 (topsoil) and 11 (background) are also remapped based on the planet seed.
The planet seed is always in the range 11111..32222 and is used to define (among other things) both the background tile (4) and topsoil texture (5) using outline bitfields. The sub() function is grabbing the first 3 character, and last 2 character for background and topsoil tiles respectively. The background tile colours should always be quite dark, so the range of the seed is chosen so that the first character is always either 1 (blue) or 2 (dark purple), and occasionally 3 (dark green). This also fits nicely into the signed int16 range (maximum: 32767).
Hold up... what's going on with this guy's long leg?
The legs are made out of a comma and pipe | characters hidden by the body. But in the last frame, the body does not hide the top half of the |, so I shifted it two pixels down and it gets clipped out by spr().
Automaton Rules
The rules are applied to 25% of tiles near the player each frame, so each visible tile updates at ~7.5 frames per second:
- blast sparkles disappear
- lava, sand, and explosives fall into empty tiles (value < 6)
- lava that is on a solid tile can randomly pool sideways into empty tiles
To create explosions, the spritesheet is copied to the screen, 3 black circles are drawn (at -128, +0, +128 to handle the world looping), and then copied back to the spritesheet. The frame is then skipped and redrawn to avoid showing any of that process during play.
for x=-22,22 do
-- (draw tiles here)
x=(x+px)&127 -- new local copy x in looped world space
for y=12+tt%4,120,4 do
if(sget(x,y)>11)sset(x,y,0) -- 12 blast sparkles
-- 2 bomb 3 lava 7 sand
--> check of tiles is in this set using bits 0b10001100 (140)
b1=sget(x,y)
if(1<<b1)&140>0 then
if sget(x,y+1)<6 then
-- empty space or lava below -> fall
sset(x,y+1,b1)
sset(x,y,0)
elseif b1<3 then
-- explosive moving in to a solid tile
memcpy(0x6000,0,0x2000)
c(x,y,3,12)
c(x+128,y,3,12)
c(x-128,y,3,12)
memcpy(0,0x6000,0x2000)
?"\as▥i6x5[a"
goto _
elseif b1<4 then
-- lava: pool sideways
x2=(x-1+rnd(3))%128
if(sget(x2,y)<4)sset(x2,y,b1)
end
end
end
end |
Platformer Physics
Very tiny but serviceable platformer logic following ideas from Blue and Lefty and other previous pico-1k games. The ? print statements are for playing sounds with P8SCII commands:
-- move horizontally
x1 = (px + dx) % 128 -- candidate x in looping world
-- move to x1 if not solid at top and bottom of player
if (sget(x1, py) | sget(x1, py+.75) < 5) px = x1
-- move vertically
if(jj > 0) then -- jumping / falling
py += dy -- speculative; if hit solid, roll back
if(sget(px, py) > 4) py-=dy dy=0 py\=1 -- indexes more than 4 are all solid; roll back and snap to top.
if(sget(px, py+1) > 4) jj=0 py\=1 ?"\as2[[[g" -- land on something solid
else
if (sget(px, py+1) < 5) jj=1 dy=0 -- start falling when there is nothing solid below feet
if (btn(4)) dy=-.6 jj=1 ?"\as2[[ga" -- jump
end |
Finally, the resulting p8.rom cartride bytes in xxd -p format, after being minified and compressed with @thisismypassword's Shrinko-8:
// pico-1k entry page on itch: https://itch.io/jam/pico-1k-2026/rate/5047251
Lexaloffle News
My blog feed has been mostly just jam games lately, so here is a quick rundown of things going on behind the scenes:
-
Picotron is going into beta at 0.3.1 later this year, and finally get its own BBS announcement post! (but I have also been detailing various Picotron developments on bsky and mastodon). 0.3.1 includes scoresub() improvements, websockets support and extra math functionality for supporting 3d renderers written in userland.
-
Doodlemud is returning as a Picotron cartridge (it started as a javascript thing before Picotron existed) for stress-testing scoresub() and the more general LXO ("Lexaloffle Online") system that will be eventually be rolled out to PICO-8 and Voxatron. You can read more about LXO here, and there is a twitch stream of a talk I did last week at Tokyo Indies that goes a bit into scoresub(): https://www.twitch.tv/videos/2878326421 . There's also a nice talk at the end by Shoma on Carrot Kingdom and its WIP Picotron edition.
-
Earlier this year I released p8rt_ios, which is a wasm module that can be used to create iOS companion apps like Pocket8 and P8Launcher. I'm also working on a .apk build targeted at playing carts on android handhelds, and later my own companion app for Android. For now there is also: Macs75/pico8-android).
-
We will be at Pixel Art Park in Yokohama 2026-10-11 (as Pico Pico Cafe) selling miscellaneous rare Lexaloffle merch including machine-embroidered jelpi socks :3
- The first postcart exhibition is happening at Pico Pico Cafe during the last week of November! There will be a separate announcement and call for postcarts, each of which will be viewable both in their regular animated form and as printed postcard-sized stills with their source code on the back.
It's actually possible to go below what I assume to be the intended bottom of the world if the player lands directly on top of the bottom layer and uses downward explosives. They don't explode it, but each one destroys one block of the bottom layer and stays there.

I think I found a bug... or a unique ground/element? I don't know how I managed to do this (bombing lava generally does not this), nor if touching it breaks the game.
Anyway, very fun 1k game. Impressive work as usual, Zep. :)
This is SO good, @zep! 🤩
The quality of the mechanics and playability in this tiny 1K entry are very impressive 🤯
Found myself progressing, dying and repeating several times (very addictive!).
I also kinda love how this sits in perfectly alongside other procgen-driven games like LMS (in fact, I originally wanted the planet layer to be platform/side-on, but I have NO idea how to squeeze it in back in the day - you just proved it IS possible! 👏)
Superb entry (...and thx for taking part! 😉)
P.S. How do u enable the cool zoomed-out/map view!? 😯
P.P.S. If u want any guinea pigs for your Android app - please do feel free to reach out! 😉 #ExcitingTimes
@zep, Thanks for the detailed writeup on this! I was wondering, though, about the raw hex format you showed. Can you paste that text into a file and run it in Pico-8, or is there a way to convert it back to plain text code that can be pasted and run?
[Please log in to post a comment]




