Log In  

I've created a cart minification & linting tool in python - Shrinko8:


If you don't want to download anything, or want to use a UI - you can use the webapp here:

Otherwise, see the github link for details on how to download & use the tool.


  • It can do aggressive minification to reduce the token count, the character count, and the compressed size of a cart, giving meaningfully better results than other known tools like p8tool or GEM.

  • (E.g. in one example, a 81,416 char cart went to 31,213 chars with shrinko8, 35,563 chars with p8tool (though p8tool didn't run on it without having to do some hacks), and 40,813 chars with GEM.)

  • It can do linting, aka reporting of issues that may indicate bugs in your code, such as undefined, unused and duplicate local variables.

  • It supports all pico8 syntax and globals, including modern ones (as of the time of writing, but I do plan to update it when needed).

  • It also supports creating a p8.png file directly, and can actually compress carts better than Pico-8 would.

  • It also now supports declaring constants and replacing constant expressions with their value during minification - including even removing 'if false' blocks - see readme for more info.


Using the web-app is hopefully self-explanatory.

Example of using the command line: (first creates p8, second creates p8.png, third minifies less but works without changes for any cart)

python shrinko8.py path-to-input.p8 path-to-output.p8 --lint --count --minify
python shrinko8.py path-to-input.p8 path-to-output.p8.png --lint --count --minify
python shrinko8.py path-to-input.p8 path-to-output.p8.png --lint --count --minify-safe-only

More information is in the readme

I've decided to share this tool since people were having trouble using p8tool on their modern carts.

Comments, issues, and contribution are welcome.

Thanks to pancelor for many bug reports, suggestions, and the tool's name.

P#114635 2022-07-22 14:38 ( Edited 2024-04-14 21:26)

Hi @thissismypassword:

I could sure use something like this for some of the more complex programs like the dialog window I made recently. Yep I manually compressed that myself. Took some time.

From the link you listed is there a way to download everything listed there you have - perhaps as a ZIP file - without downloading each file manually ?

P#114636 2022-07-22 16:04

@dw817 - yes, there's a Download Zip button under "Code" in all github projects, but I've added a link to it in the readme now so it's more visible.

P#114638 2022-07-22 16:37 ( Edited 2022-07-22 16:41)

OK, first off, big props on the fine work you did here, @thisismypassword, now that I could download it, thanks ! Definite gold star work here. Interesting idea to use lowercase letters as variable names. I'll have to remember that.

Yet - I am a little disappointed.

I thought it would take large commands such as circfill and optionally turn them into _ci or something, as I did with my code. Is there a way to do this automatically in your PY process ?

P#114643 2022-07-22 18:01 ( Edited 2022-07-23 00:23)

this tool is fantastic! it's already helping me out a lot with my current project, which is very low on compressed space

P#114653 2022-07-22 20:16

@dw817 - thanks, doing this fully automatically is too much for now (especially as it also increases the token count, so it's a trade-off), but I did add a --no-preserve option which, together with --[[preserve]], allows you to do this semi-automatically.
(You have to decide on the globals you wish to rename and add the "renames" yourself, but you can then keep using the globals by their real names and the minifier will determine the best name for them as usual).
See the new "Renaming Pico-8 Built-in functions" section in the readme for exact instructions.

P#114657 2022-07-22 23:08 ( Edited 2022-07-22 23:20)

Update: I've added the ability to output p8.png files (previously I kept this ability disabled since my compression code used to be worse than Pico-8's, but now that I improved it to be a bit better than Pico-8's - I've enabled this)

P#114693 2022-07-24 03:26 ( Edited 2022-07-25 01:16)

Glad to have a newer tool to point people to! I haven't formally given up on p8tool but I have partial PXA support that isn't working and I got kind of demoralized to fix it, so it stalled and has fallen behind. Maybe I can crib off of yours and unblock it. 😅 Nicely done!

P#114705 2022-07-24 05:45

One more update - I've added the ability to specify a custom python script to run before/after the processing, which can be useful to merge in custom data to the cart, etc.
More details (and example of use) in the README in github.

EDIT: And one more - I've added automatic removal of most unneeded parentheses, which decreases token count as part of the minification as well. (This is useful because memorizing the entire operator precedence table is hard and removing all unneeded parentheses results in hard-to-understand code)

P#114866 2022-07-26 23:24 ( Edited 2022-07-28 15:33)

I've finally got RP-8 fully migrated over to shrinko8, and the results have been amazing. p8tool, when I was using it, took off about 2k bytes, which was fantastic and unblocked my forward progress at the time. shrinko8 manages to take off another 2k or so bytes on top of that, dropping RP-8's compressed size from ~16.6k to ~12.4k.

Of the additional 2k I got from shrinko8, about half was just from better minification, the other half was from taking advantage of shrinko8's extension options to minify global names and table keys across both Lua code and parsed string data / embedded scripts. (This allowed me to exempt many fewer names from minification.)

If you need to squeeze out every byte you can, and especially if you've already been aggressive about moving structured data into strings to reduce token count, you should definitely check this project out.

edit: The linter is also great. It even found a missing local that was eating about 0.5% of the CPU budget!

P#115966 2022-08-18 03:44 ( Edited 2022-08-18 15:59)

It's been 4-months since I commented on this marvelous tool. If you are having difficulty in downloading and installing it, I've put some information HERE to help:


P#120712 2022-11-14 19:51

OK, suggestion for future Shrinko-8. Convert this:

if orange==25 then

to THIS:


Currently I am getting this:

if e==25then n=17i=34end

The easiest way to determine this is to ensure there are no additional IF statements found within the block of code that is to be Shrinko-ed and definitely no incomplete IF that requires ELSE, ELSEIF, or END on an additional line outside.

I won't be creating a new post each time. If I have further suggestions for Shrinko8, I will post them in this message.

Thanks for your attention to this !

P#120733 2022-11-14 23:34 ( Edited 2022-11-14 23:38)

@thisismypassword Thanks for this great tool. Do you have an Itch or Patreon or some other way the community can support your hard work?

P#124036 2023-01-10 03:47

I've now created a webapp for Shrinko8, so if you previously wanted to try it but didn't want to download things or to use the command line, now you don't have to:


P#131640 2023-07-04 16:44

@dw817 - Thanks for the suggestion. A bit late, but added:
Rewriting if statements into shorthand form is now done under the "--focus-chars" command line option.
(And sometimes even without it - depending on heuristics)

Plus, another feature just added to v1.1:
Shrinko8 now merges consecutive local statements, allowing you to write the more readable:

local stuff = {}
local unrelated = 3

And receive:

local a,b={},3

Which saves tokens and characters.

(Plus, it does the same for assignments under the "--focus-tokens" command line option)

P#132150 2023-07-20 02:51 ( Edited 2023-07-20 07:22)

been creating a game using this so i can fit it into a url, and ive come up with a way to get rid of some characters a bit more, but this is what ive done:

from this:

function _init()
 text="hello world!"

function _update()

function _draw()

to this (commented and indented to explain whats happening and to view it better.):

function _draw()
 --init runs once at the start of the program, achieved by running this
 --on the first frame only
 if(not a)a=true b="hello world!"c=1
 --write update function stuff write here, update runs before draw, so ----that's why it goes first
 --lastly, the draw code

version without comments and a bit less characters:

function _draw()
 if(not a)a=true b="hello world!"c=1 
 c+=1 cls() print(b..c)

the reason _draw is used is because draw has less characters, but _update60 may be used for 60 fps with basically the same result.
this could be in the aggressive minification option, though its not needed because it is not really tedious to do by hand, but it would be nice if it was automated.
I don't know if this is a bad idea to do this, if this is not efficient or not organized any way, but it doesn't give any errors, so i guess its fine.

P#134096 2023-09-09 15:52 ( Edited 2023-09-09 15:58)

I'm having an error with the program. It tried to compile commented lines, so I get an error like "unterminated brackets" when the brackets are just part of a comment.

P#134105 2023-09-09 19:10

@StrongSand94191 - can you post a reproducible example?

Are you sure you're using the latest version of both Pico-8 and Shrinko8?
There was recently(-ish) a change in how (whether) Pico-8 deals with nested block comments, and Shrinko8 now follows the latest version's behavior (aka - no nested comments).

P#134107 2023-09-09 19:43 ( Edited 2023-09-09 19:59)

@thisismypassword If I paste in my celeste mod into the Shrinko8 webapp, I get "input.p8:1810:3 Unterminated long brackets." The line this is referring to is gfx. The cartridge runs fine, and if I delete the stuff like gfx and sfx, shrinko8 works.

P#134178 2023-09-11 15:48

@StrongSand94191 - Oh, it looks like you have a "--[[" at the end of the cart there. I'm curious - why?

It looks like pico-8 treats unclosed block comments like that as a regular line comment, which is weird.

I've changed shrinko8 to do the same so your case should be fixed now.

Though I'd recommend you to get rid of that unclosed "--[[" anyway, in case pico-8 decides to error out here in the future (like plain lua does).

P#134180 2023-09-11 16:21 ( Edited 2023-09-11 16:27)

@JoVaMi - There should be subtle differences in behaviour when you put everything in just one of _draw/_update (as the mainloop will call them at subtly different times and sometimes even different amounts), so I wouldn't want to do that automatically. [*]

That said, the idea of allowing specifying an "--[[inline]]" hint to request shrinko8 to inline stuff (without having to "dirty" your code) was tossed around - though I'm not yet sure if/when I'll add it.

[*] Well, there is a good question of "you're already risking breaking carts under aggressive minification, why not also risk this behaviour change" - I guess the answer to that is that I try to limit such breakage risks to those that give a lot of benefit, and whose impact is obvious and well-understood.

P#134181 2023-09-11 16:25 ( Edited 2023-09-11 16:42)

I've now added supports for constants.

E.g. you can now write:

--[[const]] k_spr_hero = 5
--[[const]] k_box_x, k_box_y = 50, 60
--[[const]] DEBUG = false

function _draw()
    spr(k_spr_hero, x, y) -- k_spr_hero gets replaced with 5
    rectfill(k_box_x, k_box_y, k_box_x + 7, k_box_y + 11) -- becomes rectfill(50,60,57,71)

if DEBUG then -- this block gets removed unless DEBUG is true

And have Shrinko8 automatically replace constant expressions with their value.
More information is in the readme section

P#146647 2024-04-14 21:28 ( Edited 2024-04-14 21:29)

[Please log in to post a comment]