Log In  
Follow
xietanu

Having fun with Pico8 and Picotron! He/Him


If you create a new text editor using the gui library, the :detach() function of the returned editor doesn't work as you would expect it to, only detaching the content of the editor, not the editor itself. This is an issue if you create/remove a number of editors, as each created editor still uses a small amount of CPU even after being detached.

This is caused because :attach_text_editor() returns the content, not the container, but it is the container that is added to the gui.

There is a simple workaround for this - instead of using text_ed:detach(), use text_ed.parent:detach(), where text_ed is the variable holding what is returned from :attach_text_editor(). However, this is not very intuitive for the user.

A simple fix would be to overwrite the :detach() function for the content so that it detaches its parent instead. I can't see many instances where this would be an issue/you'd actually want to detach just the content?

Hope this helps!

1
0 comments



Perfect Pastry Parcels

Cart #perfect_pastry_parcels-1 | 2025-08-11 | Embed ▽ | License: CC4-BY-NC-SA
9

Help Ingrid pack a selection of delicious pastries into neat little boxes as quickly as possible! But beware, some of her customers has ordered some oddly shaped brownies...

Pack a few small boxes with A Sweet Treat, or take on the ultimate pastry challenge with The Baker's Dozen - 13 boxes, each more challenging than the last!

Each puzzle is randomly generated, meaning you can play and compete for the best time over and over. Can you do better than Ingrid herself?

Controls

Click and drag the pastries into the boxes and right click, double click or scroll to rotate them. Once the boxes are full, they automatically close and the next one arrives!

[ Continue Reading.. ]

9
2 comments



Following some experimentation, I've found a couple of issues with sandboxed apps and how they are given permissions to read/write files outside of the 'special locations'.

Loading cart with sandboxed workspace app can cause data loss

When you load a cart, Picotron helpfully reopens the workspaces you were using to edit that cart. If one of those workspaces is a sandboxed app, however, the app appears to be given write access but not read access.

This results in the app 'opening' the files, but just creating blank, new files and then immediately saving the blank versions over the original files in RAM. This happens without the user initiating a save or doing anything beyond loading the cart. This makes it very easy for a user to lose data if they save the cart before noticing the issue (yes, I am speaking from experience...)

This assumes all the loading/saving is done through wrangle_working_file, I haven't tested otherwise.

The behaviour can be replicated by creating a sandboxed copy of code.p64 and using it to edit a cart, then saving and reloading that cart.

[ Continue Reading.. ]

3
0 comments



Cart #strawberry_src-7 | 2025-08-31 | Embed ▽ | License: CC4-BY-NC-SA
40

Strawberry Src

A slightly-less-simple code & text editor
Author: Matt Sutton / @xietanu.bsky.social
Version: 1.2.3

Overview

Strawberry Src is intended as an equivalent app to Picotron's in-built code or text editors, with new quality or life improvements to make navigating and editing code and text easier.

TIP:
You can set Strawberry Src as your default editor using the default_app command in the terminal.
Go to the terminal and type:
default_app <extension> <path to Strawberry Src cart>

E.g.
default_app lua /desktop/strawberry_src.p64

Change log

Version 1.2.3

  • FIX: Crashed on startup if '/ram/shared/theme.pod' didn't exist.
  • FIX: Cursor would somtimes jump to the end of the row when running the cart.
  • FIX: Scroll would not be applied properly to gui elements that were added to containers that were already scrolled. (This sometimes affected bookmarks, making their scroll stop working properly).
  • FIX: Bookmarks would stop displaying if the current group got taller than the screen height.

Version 1.2.2

  • FIX: Strawberry Src now fully works sandboxed, and the message given when running it sandboxed has been removed. A few features that require file system access are not available, but these are minor. Big thanks to Zep for all the work on recent versions to make this possible!
    (If you want to unsandbox it to get those features, right-click on your local copy and click 'about strawberry_src.p64', then uncheck the box in the bottom-left corner.)

  • FIX: Added a workaround for the default app bug, so now you can open files in Strawberry Src without making it your default app.

Version 1.2.1

  • FIX: Fixed a crash that could occur when a string preview was generated for a multi-line string. Multi-line strings currently not supported for string previews. Thanks to @klautless for the bug report.

Version 1.2.0

  • FEATURE: Added string previews. Hover over strings to see how they would be rendered - useful when using control codes. Thanks to @CannonTheWildWulf for the suggestion. Option to disable provided.
  • FEATURE: Added TODO: tracking in code files. Comments with TODO: are highlighted and logged, and can be seen in a sidebar similar to bookmarks. Clicking on the task will jump to it, clicking on the checkbox will complete it and clicking on the x will delete it.
  • FEATURE: Added ctrl-pageup/pagedown to jump to the previous and next function defined.
  • FIX: Fixed a freeze caused by the character ' and syntax error checking in some circumstances. Thanks to @demiskeleton for reporting the bug.
  • FIX: General improvements to performance, especially with dense and large files. Should now maintain a stable 60fps under all reasonable circumstances. Thanks to @Sounds for help with this.

Version 1.1.0

  • FEATURE: Added file previews. See features for more info. Thanks to @CannonTheWildWulf for the suggestion.
  • FEATURE: Added ctrl-click/middle click on filepath strings to open the related file.
  • FEATURE: Added filepaths to autocomplete, so if you start typing a string with a filepath, completions based on your filesystem will be offered. Thanks to @klautless for the suggestion.
  • FEATURE: Strawberry Src now alerts you if you're running it sandboxed and prompts to unsandbox it.
  • FIX: Fixed a rare crash related to the syntax error checking.
  • FIX: Fixed some issues related to saving while sandboxed (however, other bugs beyond my control mean it should still not be used sandboxed)

Version 1.0.0

  • Initial release

[ Continue Reading.. ]

40
16 comments



As the title says, attempting to unpod the string, "str", whether typed directly or stored in a variable causes either an out of memory error, or fully crashes picotron to the desktop - though the latter is rarer.

Attempting to unpod other strings just returns nil, which seems like the behaviour I would expect?

I've not found any other strings which cause this behaviour.

Hope this is helpful!

1 comment



When using batch drawing with sprites (i.e. supplying spr with a userdata), the sprites are drawn ignoring how camera is set using camera().

For example, if the userdata gives the position x=10, y=10, the sprite will always be drawn 10 pixels from the top of the screen and 10 from the left, regardless of how camera() has been set.

This isn't how other batch drawing operations work (which do seem to respect the camera) or how spr() works normally, and also doesn't (by my reading) match what's documented here: https://www.lexaloffle.com/dl/docs/picotron_gfx_pipeline.html. As such, I assume this isn't intended and is a bug?

1
0 comments



Cart #dues_fireworks-0 | 2025-04-13 | Embed ▽ | License: CC4-BY-NC-SA
27

Fireworks over Düsseldorf

A simple demo/screensaver that came about from a desire to get my head around userdata operations, particle systems and batch drawing, along with some affection for my current home.

Has five different displays, each of which have some variations. Cycles through them randomly, tetris-bag style.

Code is lightly commented in case anyone wants to take a look!

27
3 comments



A bit of a weird one...

I'm getting random results from code that doesn't have any random/changing elements.

I have a cart that, whenever I run it, it has one of two behaviours, apparently at random? However, the code doesn't involve any randomness (and I seeded the random number generator just in case), and doesn't load anything from the disk or mess around with memory in weird ways - I would expect the results of this code to be completely deterministic prior to user input. It should just execute one line at a time, with all the same initial values. However, the result is something that is inconsistent.

This is demonstrated below. All I am doing below is hitting ctrl-r to run the cartridge. You can see when this happens by the timer at the bottom. Each time I run it, shows one of two behaviours, seemingly at random:

This happens both when running the cart from the RAM and running it normally, it's just easier to demonstrate from the RAM.

The code for determining the dimensions of the white rectangles is a bit complicated and involves quite a few recursive calls, which I'm guessing might be the cause of this? e.g. behind the scenes Picotron's doing some stuff in parallel that's leading to a weird race condition?

[ Continue Reading.. ]

2
1 comment



Cart #spaghetticode-2 | 2025-02-10 | Embed ▽ | License: CC4-BY-NC-SA
37

SpaghettiCode - a Picotron code editor

v0.2.0 - alpha

SpaghettiCode is a code editor for Picotron that aims to provide more features than the built-in code editor, providing a little extra help and quality of life.

Important notes before you start using SpaghettiCode!

SpaghettiCode is in alpha

Please back up your carts before editing them with SpaghettiCode! I am not aware of any bugs or issues that could lead to corruption or data loss, but until it has seen a bit more of a shakedown, I don't want to risk being responsible for anyone losing work.

Also, let me know if you run into weird issues or have ideas! Feedback would be greatly appreciated.

[ Continue Reading.. ]

37
25 comments



As part of a project I'm working on, I wrote a quick json parser that loads a json file into a picotron table and thought it might be worth sharing.

E.g.

{
	"a":{"b":1,"c":-3.5},
	"d":["e",true,false]
}

is converted to:

{
	a={
		b=1,
		c=-3.5
	},
	d={"e",true,false}
}

So if you have some data in json format already, it's easy enough to load in. Hopefully a bit useful for storing configurations or data needed by carts outside of the code itself for neatness.

It should follow json specifications (e.g. whitespace outside of strings doesn't matter), and any null values will be handed as picotron's nil - however, because of the ways nils are added to tables, they won't work/appear in the way it does in the json itself.

Also, if the parser runs into any issues, it tries to offer some helpful errors to identify where the problem is.

I've done some light testing of the code, but let me know if you run into any issues with it.

Code is hidden below:
[hidden]

function init_json_reader()
	J_WHITESPACE = {}
	J_WHITESPACE[" "]=true
	J_WHITESPACE["	"]=true
	J_WHITESPACE[chr(10)]=true
	J_WHITESPACE[chr(13)]=true

	J_START_TABLE = "{"
	J_STOP_TABLE="}"
	J_START_LIST="["
	J_STOP_LIST="]"
	J_QUOTE="\""
	J_COLON=":"
	J_COMMA=","
	J_OBJ_STARTS={
		n=read_json_null,
		t=read_json_true,
		f=read_json_false,
	}
	J_OBJ_STARTS[J_QUOTE]=read_json_key_string
	J_OBJ_STARTS[J_START_TABLE]=read_json_table
	J_OBJ_STARTS[J_START_LIST]=read_json_list
	J_OBJ_STARTS["-"]=read_json_number

	for i = 0,9 do
		J_OBJ_STARTS[tostr(i)] = read_json_number
	end
	json_init = true
end

function load_json_file(filepath)
	-- Load and read a json file and return a list or table
	if not json_init then init_json_reader() end
	local text = fetch(filepath)
	assert(text!=nil,"Failed to load json file: "..filepath)
	return read_json(text)
end

function read_json(string)
	if not json_init then init_json_reader() end
	-- Read a json string and return a list or table.
	if #string == 0 then
		return nil
	end

	local i=skip_json_whitespace(string,1)

	if string[i] == J_START_TABLE then
		return read_json_table(string,i)
	elseif string[i] == J_START_LIST then
		return read_json_list(string,i)
	else
		assert(false,"Unexpected initial character encountered in json file: "..string[i])
	end
end

function skip_json_whitespace(string,i)
	-- Skip to the first non-whitespace character from position i
	while J_WHITESPACE[string[i]] do
		i+=1
		assert(i<=#string,"Unexpectedly hit end of file while skipping whitespace\nin json file")
	end
	return i
end

function read_json_table(string,i)
	local eot = false
	local tab = {}
	local k, v = nil, nil

	if string[i]==J_START_TABLE then
		i+=1
	end

	while not eot do
		k, v, i = read_json_table_entry(string, i)
		tab[k] = v
		i = skip_json_whitespace(string,i)
		if string[i]==J_COMMA then
			i+=1
		elseif string[i]==J_STOP_TABLE then
			i+=1
			eot=true
		else
			assert(
				false,
				"Unexpected character encounted after reading json entry with\nkey '"..tostr(k).."': "..tostr(string[i]).." "
			)
		end
	end
	return tab, i
end

function read_json_table_entry(string, i)
	local k, v = nil, nil
	i = skip_json_whitespace(string,i)
	k, i = read_json_key_string(string,i)
	i = skip_json_whitespace(string,i)
	assert(
		string[i] == J_COLON,
		"Expected colon following json key '"..k.."', found: "..string[i]
	)
	i = skip_json_whitespace(string,i+1)
	assert(
		J_OBJ_STARTS[string[i]]!=nil,
		"Unexpected value encounted while reading json entry\n'"..k.."', found: "..string[i]
	)
	v,i=J_OBJ_STARTS[string[i]](string,i)
	return k, v, i
end

function read_json_key_string(string,i)
	assert(
		string[i]!=J_STOP_TABLE,
		"Table ended while expecting entry, make sure you don't have a misplaced comma."
	)
	assert(
		string[i]==J_QUOTE,
		"Expected json key/string to start with double quote,\ninstead found: "..sub(string,i,i+10).."..."
	)
	i+=1

	local s = i	

	while string[i]!=J_QUOTE do
		i+=1
		assert(
			i<=#string,
			"Encountered end of json while reading key/string:\n"..sub(string,i,i+10).."..."
		)
	end
	return sub(string,s,i-1), i+1
end

function read_json_list(string, i)
	local eol = false
	local lis = {}
	local value = nil

	if string[i]==J_START_LIST then
		i+=1
	end

	while not eol do
		i = skip_json_whitespace(string,i)
		assert(
			string[i]!=J_STOP_LIST,
			"List ended while expecting entry, make sure you don't have a misplaced comma."
		)
		assert(
			J_OBJ_STARTS[string[i]]!=nil,
			"Unexpected value encounted while reading json list,\nfound: "..sub(string,i,i+10).."..."
		)
		value,i=J_OBJ_STARTS[string[i]](string,i)	

		add(lis,value)

		i = skip_json_whitespace(string,i)
		if string[i]==J_COMMA then
			i+=1
		elseif string[i]==J_STOP_LIST then
			i+=1
			eol=true
		else
			assert(
				false,
				"Unexpected character encounted after reading json list entry: "..string[i]
			)
		end
	end
	return lis, i
end

function read_json_null(string,i)
	assert(sub(string,i,i+3)=="null","Was expecting to read null during json file read, instead\nfound: "..sub(string,i,i+10).."...")
	i+=4
	return nil, i
end

function read_json_true(string,i)
	assert(sub(string,i,i+3)=="true","Was expecting to read true during json file read, instead\nfound: "..sub(string,i,i+10).."...")
	i+=4
	return true, i
end

function read_json_false(string,i)
	assert(sub(string,i,i+4)=="false","Was expecting to read false during json file read, instead\nfound: "..sub(string,i,i+10).."...")
	i+=5
	return false, i
end

function read_json_number(string,i)
	local s = i

	while not (
		J_WHITESPACE[string[i]] or 
		string[i]==J_COMMA or 
		string[i]==J_STOP_TABLE or
		string[i]==J_STOP_LIST
	) do
		i+=1
		assert(i<=#string,"Unexpectedly hit the end of json string while reading a number.")
	end

	return tonum(sub(string,s,i-1)), i
end

[ Continue Reading.. ]

4
3 comments



Cart #string_theory-1 | 2025-01-19 | Embed ▽ | License: CC4-BY-NC-SA
9

String Theory

A simple productivity app for making evidence boards - cards with information linked by string.

A very normal way of mapping all kinds of information - stories, families, conspiracies, etc.

Includes the ability to save, load and customize boards in a variety of ways.

(Mostly made to help learn Picotron, but hope it might be fun/useful for someone!)

Find me on Bluesky

9
4 comments



Cart #terra_nova_pinball-2 | 2022-12-02 | Code ▽ | Embed ▽ | License: CC4-BY-NC-SA
189

Terra Nova Pinball

Ricochet around an alien world in this Pico-8 pinball table.

Basic Gameplay

Launch the ball onto the table using ⬆️/⬇️ to control the launch power and 🅾️/❎ to release the ball.

Use the flippers (⬅️/➡️ by default, can be configured in the menu) to keep the ball from draining out the bottom of the table, hitting targets and bumpers and completing minigames (see below) to score points.

You have 3 balls to score as many points as you can, and record a new highscore!

If the ball drains within 15s of launching, you get a free relaunch.

[ Continue Reading.. ]

189
36 comments



Cart #quincunx_xietanu-0 | 2022-07-27 | Code ▽ | Embed ▽ | License: CC4-BY-NC-SA
20

Quincunx - a Bean Machine

A Quincunx, otherwise known as a bean machine or Galton board, is a device designed to demonstrate how bell curves appear from combinations of random events, and so why they appear so much in nature.

A physics-based statistics toy

This cart (roughly) simulates marbles bouncing through a grid of pegs and landing in bins at the bottom - for anyone who enjoys watching marbles fall or maybe wants to demonstrate some statistical principles themselves.

The bins have a bell curve overlaid on them that should match the approximate pattern of the balls once they have all fallen, so you can test how consistent the simulation is with the theory.

[ Continue Reading.. ]

20
3 comments