A picture of Penny from Pokémon Scarlet and Violet. She is a girl with glasses and red and blue hair. Arnaught A rainbow tilted to the right.

The Year of the Picotron Desktop

March 14, 2025 | Tags: personal picotron programming


1 year ago, on March 14, 2024, Picotron released! I had a lot of fun messing around with everything in the days after its release. On the 20th, I wrote up a blog post about some of the programs I made in it: a DVD screensaver, a bunch of cli utilities, a wordle clone, a very basic and janky Mastodon client, and a pipes screensaver. I’ve also posted on this blog about my pretty_printh function to print with ANSI codes.

This blog post will be a showcase of a bunch of the software I’ve made in Picotron in the past year (including some that haven’t been fully published yet!). I’ll also talk about how the way that I personally customize and use Picotron. If you follow me on the Fediverse (or if you’ve seen my posts on the Picotron BBS), you may have seen a bunch of this stuff before.

As I started writing the blog post, I realized that I made a lot of things in Picotron this year! So I’ll try to be brief on most things. Most cartridges will be embedded here, and can be played directly from this post. (The cart image is used as a fallback on smaller screens.)

Projects and Cartridges #

Picotron Utilities #

Picotron Utilities (GitHub, BBS) is one of the very first things I started working on in Picotron.[1] A lot of it is the same from where it was a year ago, but there are also a lot of improvements. Three new utilities were added (echo, fd, and stat), and a bunch of improvements (such as colorized output on most commands).

Many of these utilities make using the Picotron terminal way nicer. touch provides an easy way to create files from the terminal, grep makes it possible to do a project wide search, fd makes it easy to search for a file name, and tree lets you get an see an overview of a folder.

A screenshot showing the directory tree of the Wordle source code.
Thoughts on the file icons? I kinda like them.

Fuzzy Finder #

Fuzzy Finder (GitHub, BBS) is a fuzzy finder utility I made, ala fzf. It lets you do a fuzzy search for files, and quickly open them.

Picotron Remote Terminal #

Picotron Remote Terminal (GitHub, BBS is one of my first attempts to control Picotron remotely from the host to make external editing easier. There is (at least, as far as I’m aware) no way to watch for an external change to a file or to get a message from the outside without polling. So that’s what it does. It polls a web server for commands, and executes them when they’re received.

Before landing on a webserver, I tried using a named pipe (doesn’t work, locks up Picotron). You could also get it working with polling a instead, but being a web server brings one cool benefit: you can run host commands from inside Picotron! It’s a neat trick to access git from inside Picotron

I might try reworking this when TCP / websockets support is added.

Picotron Definitions #

Picotron Definitions is a set of LuaLS definition files I’ve written to make it possible to use a language server with Picotron projects.

8Ball and Snowglobe #

Magic 8 Ball (BBS) is a simple little demo I made to mess around detecting when the window moves. It was also kinda inspired by Clairvoyant by Cassidy James Blaede. I thought it would be a fun application to make, and it would cool if you could shake the window to get the answer!

Shake the window, and get your answer!
Shake the window, and get your answer!

Snowglobe (GitHub, BBS) is similar. It uses the same window shaking trick as 8ball, but this time shaking causes snow to fall!

Shake the window to make it snow!
Shake the window to make it snow!

PUSH #

PUSH (Picotron Upgraded SHell) (GitHub, BBS) is one of my favorite things I’ve made in Picotron.

Over the past year, I ended up switching my shell from Bash to Fish. Fish has a lot of really useful shortcuts (basically everything under Shared Bindings), and I wanted to include some of them in Picotron. (Like pressing Alt+L to list the directory under the cursor, or cd history) But with some of the changes I wanted to make, there’s no way easily patch the terminal with something like sedish. So, I modified terminal.lua to include a modloader, and used that to make the changes I wanted. I have a bunch of examples in the readme for things PUSH can do (and by “examples”, I just mean my setup. I use all of them lol).

Pico 1K Jam 2024 #

Balloon Run (Itch, BBS) is a fun little game that I made for Pico 1K Jam 2024. Pico 1K Jam is a game jam where you have to make a game in Pico-8 or Picotron in 1KiB (1024 bytes) or less. I made a Balloon Fight-ish game, and fit it in 1021 bytes.

1KiB Gaming
1KiB Gaming

Advent of Code 2024 #

2024 was my second year participating in Advent of Code. I decided to do it in Picotron (the year before, I did it in TypeScript and a little C++).

I chose to do it in Picotron because the year before I saw some cool looking visualizations in Pico8, and I wanted to try making a solution with a visualization. I never published it as a cart, but it should be runnable from source, if you’d like to run my solutions.

A screenshot of my Advent of Code 2024 runner. 35 out of 50 stars are unlocked. There's a snowfall effect and a picture of a Christmas tree to the right side.
Did you notice the snowfall effect and Christmas tree are from Snowglobe?

Trash Manager #

Trash Manager (GitHub, BBS) is a program that implements trash in Picotron, loosely following the XDG Trash Spec.

When sent to trash, it moves the files to the trash directory (/appdata/trash/files) and drops an info file in the info directory (/appdata/trash/info). The info file contains the original path of the file and deletion date, allowing the file to be restored. Filenav.p64 includes a basic trash implementation where deleted files are moved to /ram/compost/, but because this is in RAM, it gets wiped when you close Picotron.

Trash Manager can be used in a bunch of different ways: a CLI app, a GUI app, a tooltray indicator, and even integrated directly in Filenav.p64 using a sedish script.

extload and extrunner #

extload and extrunner (BBS) is the solution I ended up landing on for working with an external editor.

According to the docs, one of the recommended ways of using an external editor is to simply include the files from an external folder. However, this requires you to make changes to your code, and remove all the external includes before publishing.

extload is a modified version of load.lua that loads an external folder by saving the path of the folder you want to load, and loading the extrunner cart instead. extrunner is a dummy cart that cds into the external folder, and includes the code. /system/lib/resources.lua is the file that loads the graphics and audio from the cart, so including it again will load it from the external folder, and including main.lua will include all of the code.

This is a really simple and convenient method. Nothing special needs to be set up on the host. The only requirement is that the cart is extracted!

Bouncy Ball #

Bouncy Ball (BBS) is a fun little desktop toy. It spawns a bouncy ball that you can throw around on your desktop!

You can move a window automatically with window({ x=x, y=y }). I have a basic physics simulation keeping track of the position of the window and automatically moving it. Throwing it is handled by calculating the delta of the mouse in the move event. (You can’t use mouse() for this, as coordinates are given relative to the window position! The move event gives absolute position.) Window collision is done by reading /ram/shared/windows.pod, which contains the currently opened window positions.

Originally, my plan for this was to be a toy you could put on your tooltray. While you can put this on the tooltray, it doesn’t work very well. It will drop behind the shade and you won’t be able to see it! So, I decided to rework it for the desktop and added window collision.

Fun fact: the collision here is largely the same as in Balloon Run! For size reasons, Balloon Run only checks collision for half the character. If you stand on the left edge of a platform, you’ll fall through! This was also a problem in Bouncy Ball, so I ended up fixing it here.

There was also a bug where if you got clipped into the ground the player would vibrate. This is because of how bouncing is implemented. When you hit a wall, floor, or ceiling, your speed in that direction will be inverted. However, if you are clipped into an object, you are constantly having ceiling collisions, meaning your vertical speed is being continually inverted causing the player to vibrate. I fixed this in Bouncy Ball by changing it so that ceiling collisions take the absolute value of your vertical speed[2] instead of inverting it.

Bouncy, bouncy, bouncy!
Bouncy, bouncy, bouncy!

Calendar and Clock Widgets #

The calendar widget (BBS) and clock widget (BBS) are two little widgets I made to fill out my tooltray. I like analog clocks, so I wanted to replace the clock widget with an analog version. (Also, I like 12 hour time, and the default clock widget was 24 hour!)

A screenshot of my Picotron tooltray. There are several widgets, including a color reference, character reference, control reference, pomodoro timer, calendar, analog clock, trash can, owl, and notepad.
You may not like it, but this is what peak productivity looks like.
Tooltray Widget Credits

Libraries and Cool Tricks #

Startup Folder #

Something that a bunch of programs do to make configuration easier is splitting a config file into different files. Often, this is done with a config folder (such as something like ~/.bashrc.d/ or ~/.config/fish/conf.d/).

I have the startup script (/appdata/system/startup.lua) set up to load files from a folder (/appdata/system/startup/), so that my startup files are easier to manage.

startup_folder = "/appdata/system/startup"
if fstat(startup_folder) == "folder" then
	for file in all(ls(startup_folder)) do
		filename = startup_folder .. "/" .. file
		if fstat(filename) == "file" and file:find("%.lua$") then
			create_process(filename)
		end
	end
end

Before drag and drop tooltray installation was added in 0.1.1e, I had different scripts for each tooltray widget. Now, I have 4 main startup files:

Easy to Access Logs #

Using printh(), you can print to stdout. This can be very useful for making logs or debugging programs (especially in situations where you can’t/it doesn’t make sense to print debug info onscreen).

However, there’s typically no way to access this unless you launch Picotron from the terminal (which I often forget to do). I have a picotron-logging script that saves the output to a temp file (/tmp/picotron.log), so that the log is always accessible.

#!/usr/bin/env bash
picotron > /tmp/picotron.log 2>&1

Then, to view the log, I do tail -f /tmp/picotron.log, which will print the log and automatically print new changes!

INI Parser #

INI Parser (BBS) is a function to load INI(-ish) files into a lua object. The idea is that you can add comments to an INI file, unlike a POD or JSON, which may make them better for config files.

Inline Image Editor #

Inline Image Editor (BBS) is a tool to create one-off characters. I know there are a few of these made for Pico-8 (which supports the same format for one-off characters), but I hadn’t seen one for Picotron, so I made one.

\^:75277500363e1c08
\^:75277500363e1c08

Work in Progress #

Archive Utility #

Archive Utility (GitHub) is a tool that can extract .tar, .gz, and .tar.gz files. It uses LibDeflate to decompress the gz archives.[3] This library works out of the box in Picotron (but you have to provide your own sorting function, as table.sort is not in Picotron). I’m also using this CRC32 utility to verify the gz checksum.

Technically, everything is working. DecompressGZ() can read and decompress a gz file (also supports multiple concatenated gz files!). ExtractTar() can read and extract the files from a tar archive. The default behavior of the cart right now is to extract a .tar or .tar.gz into a folder.

You can even download and extract archives from the internet (such as from GitHub!)

Honestly, I’m not sure how far I want to go with this. I probably want to make a GUI mode that lets you preview an archive without writing its files, but IDK if it would make sense to create an archive from inside Picotron.[4]

Cointris #

I’m working on a Coin Conveyor (from Super Mario Party Jamboree) clone, working title “Cointris”. I have the conveyor belt, the playfield, and completing lines working. Completing a line of coins will destroy them, and destroying a bomb will destroy the 3x3 area around the bomb.

I think it’s pretty close to a playable version: just needs scoring and some polish (such as an animation/particle effect when the coins break; It’s pretty jarring for them to just disappear).

A video of the Cointris demo. Coins are placed on the playfield, and are destroyed when a line is complete.

Pac-Man Thing #

I don’t really know where I’m going with this one. I started with making something Pac-Man-ish. But I wanted to see if there was a way to have the sprite rotate without needing different sprites for all 4 directions. I ended up finding this sprite rotation library, and I started playing around with it, making this little demo where Pac-Man rotates and can be launched in the direction he’s facing.

After playing around with it for a bit, I liked the way I had it set up to bounce off the walls, and decided to make the Bouncy Ball cart.

But this demo seems like it might serve as a nice base for a pool or maybe golf game.

A video of the Pac-Man thing. Pac-Man rotates, and is launched in the direction he's facing.

LulPeg #

LPeg is a lua library based on Parsing Expression Grammars. It can be used to create more involved parsers.

LulPeg is a port of LPeg written in pure lua. With a little effort, I was able to get it working in Picotron! Though, if I’m being honest, I don’t really have anything to use it for. I mainly wanted to see if it was possible (and it is!).

Weather #

When I was making the clock and calendar widgets, I kinda wanted to make a weather widget to go along with them. I found Open-Meteo, which seems to provide a nice weather API, but there’s a bug in Picotron where fetching long URLs causes Picotron to crash. So, I’m not going to bother with this until that’s fixed.


  1. The initial commit was on March 15, 2024! ↩︎

  2. Remember, in Picotron positive Y is down! ↩︎

  3. Technically, DEFLATE is the same compression algorithm used in zip files, so it could also support zip files. ↩︎

  4. When you write a file in Picotron, it includes a metadata comment in the first line. This doesn’t really matter, but it might cause problems if you want to read the file from an outside program. ↩︎