Log In  
[ :: Read More :: ]

hi, i wrote an interactive error screen for picotron! on error, it lets you explore the stack, local variables, and shows the source location where the error occurred. use arrows to go up and down the stack, the mouse wheel to scroll, and click on table variables to expand them. you can also press space or x to toggle small-font mode.

Cart #error_explorer_example-0 | 2024-04-26 | Embed ▽ | License: CC4-BY-NC-SA

download: https://github.com/snowkittykira/picotron-error-explorer

to use it, download error_explorer.lua and include it after defining your _init, _update and _draw functions.

it replaces these functions which wrappers that call them in a coroutine, so that when errors happen the coroutine can be inspected. on error, it does its best to reset the graphics state, but there might still be cases where this fails so let me know if you run into any issues!

it relies on the following functions from lua's debug library, so i hope they will continue to be available:

  • debug.traceback
  • debug.getinfo
  • debug.getlocal
  • debug.getupvalue

(note: please use the github version and not the one in the example cart, as it'll be more up to date)

P#147383 2024-04-26 17:36 ( Edited 2024-04-30 21:38)

[ :: Read More :: ]

this crashes picotron when run:

function _init ()
P#147388 2024-04-26 17:12

[ :: Read More :: ]

hi, i'm loving picotron so far! i just wanted to report some issues using coroutines.

when using coroutine.resume, it can sometimes return early when the coroutine runs for a while due to how picotron does timeslicing. from reading head.lua, i learned that using coresume instead of coroutine.resume fixes this, which makes me think maybe coroutine.resume should be replaced with it. another more minor issue is that coresume only returns the first value of a multiple-value return/yield from the coroutine, and not any more.

here's a simple test case:

local function long_running_coroutine ()
  for i = 1, 100 do
  return 1, 2

function working ()
  local c = coroutine.create (long_running_coroutine)
  coresume (c)
  assert (coroutine.status(c) == 'dead', 'this works')

function not_working ()
  local c = coroutine.create (long_running_coroutine)
  coroutine.resume (c)
  assert (coroutine.status(c) == 'dead', 'this doesn\'t work')

function also_not_working ()
  local c = coroutine.create (long_running_coroutine)
  local a, b, c = coresume (c)
  assert (a)
  assert (b)
  assert (c, 'only one coroutine return value returned')

function _draw ()
  working ()
  not_working ()
  also_not_working ()
P#147216 2024-04-23 22:47 ( Edited 2024-04-24 02:00)

[ :: Read More :: ]

hi! when i use lua i usually use a library that prevents me from accidentally using/setting globals that aren't already defined, in order to cut down on typos. i've collected a small whitelist of globals that need to be available for picotron. i noticed in terminal.lua there's a comment saying it's not supposed to set any globals, so i thought i'd report them in case they should be fixed.

here's what i have so far in my whitelist:

-- terminal.lua
-- gui.lua

and here's the full script i'm using to protect globals in case it's helpful:

-- if you're using include(), include this after including everything else,
-- since most included modules will need to assign global variables on load

-- these globals are needed by picotron
_init = _init or false
_draw = _draw or false
_update = _update or false
-- these are used by the picotron terminal
cproj_draw = cproj_draw or false
cproj_update = cproj_update or false
_ = _ or false
_is_terminal_command = _is_terminal_command or false
k = k or false
res = res or false
-- gui needs these
drag_t = drag_t or false

local function unknown_variable (t, k)
  error (debug.traceback ('unknown variable ' .. tostring (k), 2))

setmetatable (_G, {
  __index = unknown_variable,
  __newindex = unknown_variable,

it's probably a good idea not to use it in finished carts because then changes to picotron might break them

P#147073 2024-04-21 17:21 ( Edited 2024-04-21 17:25)

[ :: Read More :: ]

I've been writing an archetype-based ecs (entity-component-system) library for picotron and i put it up on github!


It keeps component values in picotron userdata so that for simple cases like velocity / acceleration, you can use userdata operations to make it very fast! As a result it is probably slightly harder to use than the other ecs libraries available for picotron right now (which use tables to represent entities), but should allow some pretty good optimizations.

you can see the example from the readme running here:


and my plink cart also uses it, though it doesn't really need the optimization:


P#146986 2024-04-20 00:13

[ :: Read More :: ]

Cart #ecs_waterfall-0 | 2024-04-19 | Embed ▽ | License: CC4-BY-NC-SA

a little demo of an ecs system i'm working on that uses userdata to store component fields

it's 4k particles, hold the '`' key to see cpu usage for update and draw (edit: oops this key only works on desktop apparently, but it's currently at 16% update and 33% draw)

P#146970 2024-04-19 19:29 ( Edited 2024-04-19 19:31)

[ :: Read More :: ]

playing around with physics and note()

(sound is pretty borked on the bbs, so download it with load #plink) sound seems fixed yay!

P#146845 2024-04-17 21:54 ( Edited 2024-04-18 17:26)

[ :: Read More :: ]

i'm trying to use :copy when growing a userdata:

local a = vec(1, 1)
local b = vec(0, 0, 0)
a:copy (b)
-- b is still zero

i figured maybe the vectors needed to be the same size but neither of these work either:

local a = vec(1, 1)
local b = vec(0, 0)
local c = vec(0, 0)
a:add (0, b)
a:copy (c)
P#146798 2024-04-16 21:53

[ :: Read More :: ]

just noting that the following crashes picotron:

function _draw ()
    print (tostring (1 * vec(1, 1, 1)))
P#145285 2024-03-31 20:44 ( Edited 2024-03-31 20:45)

[ :: Read More :: ]

not sure if this is a bug in picotron or just in the docs, but in the gfx pipeline doc it says that the colour tables are accessed like this:

out_col = peek(0x8000+coltab*0x1000 + target_col*64 + draw_col)

but to get the colour table working properly in practice i have to swap the target and draw colours like this:

function c_set_table (draw_color, target_color, result)
  poke (0x8000 + 64 * draw_color + target_color, result)
P#145141 2024-03-30 17:00

[ :: Read More :: ]

Cart #simple_dither-1 | 2024-03-30 | Embed ▽ | License: CC4-BY-NC-SA

thought i'd post a demonstration of generating simple dither patterns for fillp in case it was useful to anyone!

feel free to use under the CC license or mit license or public domain


-- threshold map from https://en.wikipedia.org/wiki/Ordered_dithering
local threshold_map = {
   0,  8,  2, 10,
  12,  4, 14,  6,
   3, 11,  1,  9,
  15,  7, 13,  5,

local function make_mask (value)
  local mask = 0
  for i = 1, 16 do
    mask = mask * 2
    if threshold_map [i] >= value then
      mask = mask + 1
  return mask

local dither_masks = {}
for i = 0, 16 do
  dither_masks[i] = make_mask (i)

-- value 0-1
function dither_mask (value)
  return dither_masks [mid (0, math.floor (value * 16 + 0.5), 16)]


include 'dither.lua'

local W, H = 480, 270

local color1 = 9
local color2 = 30

function _draw ()
  for i = 0, H-1 do
    fillp (dither_mask (i/H))
    rectfill (0, i, W, i, (color1 << 8) | color2)
  fillp ()
P#145139 2024-03-30 16:39 ( Edited 2024-04-01 13:17)