ECHO
This is a system for collecting and printing messages ("echoes"). It is useful for debugging or displaying multiple values during gameplay without cluttering the code, allowing for quick logging and dynamic feedback.
(This tutorial was written in collaboration with RB)
What's wrong with using print()
?
The problem with printing values while debugging, as you may have already experienced, is that most values you need to check are somewhere in your update function, and if you try to print them at that point to your screen, the prints just get erased by cls()
in the draw function. Alright, to solve that, you just need to make sure you print those variables somewhere in the draw function, but by the time those variables are printed, they may have changed values once or more times, so now it's a problem of needing to print the values exactly what they were, at certain points in your code.
To solve that, you could make some debug variables such as debug1, debug2, debug3,
and then set those variables to save the values at certain points in your code. Then down in draw you print each of those variables. But now you either don't have enough specific debug variables or you have too many cluttering up your code and you have to keep track of which ones are already being used, adding to your mental strain while hunting for a bug. Quite a hassle.
The echo system solves all of these problems so you can keep your time and attention on the actual debugging issues.
Is this better than using printh()
?
The most common suggestion for printing debugging messages is to use printh() which is a built-in function for printing to the console, outside of PICO-8. But the console is not automatically started with PICO-8 by default so there are extra steps to get that set up and working. If you are already using an external code editor then you may prefer to use this method. But if you like developing all within PICO-8 itself, or if are using the Education Edition, then the echo system here is a great way to keep things simple and print accurate values directly on screen.
Echo System Setup
To add this system into your game, simply copy the code below to declare the table and two functions, and you can paste it right at the beginning of your code so that you can easily find and remove it when your game is complete.
---------------------------debugging----------
-- echoes collection
echoes = {}
-- store a value to be printed
function echo(val)
add(echoes,tostr(val))
end
-- print all echoes
-- ...opt: c (text color, default: latest)
-- ...opt: x,y (print coords)
function print_echoes(c, x, y)
local cx, cy, cc = cursor() --pen_x, pen_y, pen_color
--set text position and color
cursor(x or cx, y or cy, c or cc)
for i=1,#echoes do
print(tostr(echoes[i]))
end
--erase all echoes
echoes = {}
end
-------------------------------------------------
Then make sure you call the print_echoes
function at the very end of your draw function so that it always prints debugging messages on top of anything drawn in your game.
function _draw()
... --all of your draw code
print_echoes() --just add this line
end
*** You can customize the print_echoes function with text color and position, or leave it on default which is whatever the cursor's last color and position is.
Lastly, wherever and whenever you want to print values to the screen as debug messages, simply write echo()
at that point in the code and include the variable you want to print and it will save the value of that variable as it was at that point in the code. You can also add a label to be printed, for example:
x = 10
function _update()
echo("x before = "..x)
x = x + 3
echo("x after = "..x)
end
For printing simple values, that is all you need. But you may find yourself wanting to print the contents of a table. Instead of looping through all the entries of the table and echoing their values, here is an extension of the echo system specifically for printing the (shallow) contents of a table at any point.
Simply pass a table and an optional label to this echo_tbl()
function at any point in your code, just like you would use the regular echo()
function. Don't forget to copy and add this function with the others at the start of your code.
-- optional: label (header caption)
function echo_tbl(tbl, label)
if type(tbl) == "table" then
add(echoes,"--")
add(echoes,"[ " .. (label or "unlabeled") .. " ]")
add(echoes,"")
--
for k,v in pairs(tbl) do
add(echoes,("+ " .. k .. ": " .. tostr(v)))
end
--
add(echoes,"--")
end
end
Debugging Tip!
Often times while debugging, you want to run your game, then stop or slow it down just before the bug happens so you can see the values and discover what is causing the bug. To do that easily, you can use stop() to exit a running game at a specific point so you don't have to try to time it by hand. You can use an if statement to determine when your game is getting close to the bug in order to stop it at the right time.
function _draw()
cls(1)
-- all your draw code
--debugging--
print_echoes()
if x>20 then
stop()
end
-------------
end
You can then advance the game frame-by-frame and take all the time you need to monitor your debugging variables like running the game in super slo-mo by using the frame stepping command. This should really help you narrow in on the bug and solve the issues fast and easy.
Echo System Explained
The Echo System uses 1 table and 2 functions:
echoes = {} |
An empty table to hold debug messages. |
echo() |
Adds message text to the echoes list. |
print_echoes() |
Prints all the stored values on the screen at specified coordinates and text color, then clears the list. |
With an optional third function:
echo_tbl() |
Adds table data to the echo list to easily print table data. |
First we create an empty table to hold all of the debugging messages:
echoes = {}
Then we will add values to that table with the "echo" function, making sure the value being saved is converted to a string data type with tostr(). You can start using this function anywhere in your code to record values exactly as they are at that point in the code to be printed at the end of the draw call.
function echo(val)
add( echoes, tostr(val) )
end
Then we print all of the messages from the echoes table with the print_echoes function.
function print_echoes(c, x, y)
local cx, cy, cc = cursor() --pen_x, pen_y, pen_color
--set text position and color
cursor(x or cx, y or cy, c or cc)
for i=1,#echoes do
print(tostr(echoes[i]))
end
--erase all echoes
echoes = {}
end
This custom print function has 3 optional parameters: c, x, y
. Those are for customizing the text color and position of the first printed line of debugging messages, your echoes. The first line of the function takes the cursor position and color to be used as the default values if you do not give the function any arguments. The parameters and default values are prepared this way so that you can call this function in 3 ways:
print_echoes() --default color and position
print_echoes(7) --set color, default position
print_echoes(8,10,10) --set color and position
After getting and using either the given arguments or the default cursor values, the function loops through the echoes table and simply prints each value. Finally, it empties the echoes table so that the next frame of your game can print all new values.
If you included the echo_tbl()
function, then you can call this function just like calling echo()
, but pass it an entire table instead of just a value. You can also include a header label to identify the table data easier in your debugging messages.
echo_tbl( player, "player table" )
The way that function works is it first checks to make sure that the first argument passed is actually a table so that it doesn't error when trying to get the table data. Then it adds a double dash just to separate the table data visually from any other echoes. If you included a label, then it will add that to the echoes, otherwise it will add "unlabeled" instead. Then it will add a blank string to echoes to create a gap between the label and the table data. Then it will loop through the table data, extracting the key and value pairs and add those together in a string as individual echoes with a colon in between. Finally, it adds another double dash to visually close the table in the echo output.
Here is what this player table would look like after echoing it using the code example above:
player | |
---|---|
x | 10 |
y | 20 |
sprite | 1 |
--
player table
x : 10
y : 20
sprite : 1
--
41
1 Jun 2025