Step by step: How to convert your image to pico-8 as map data

In detail, the process to convert your image into pico-8 can be divided in 3 steps: You’ll need ImageToPico8.

Download ImageToPico8

Sunset picture converted to pico8 with ImageToPico8

Convert your Image (or Preview and Convert)

On a Windows Command Prompt, type:
ImgToP8 -orange "D:\My folder\MyPhoto.jpg"
ImageToPico8 lets you preview the conversion of your image in the desired palette. For example -orange for an orange palette and -blue for a blue palette.
More palette presets or how to set your own palette are also made possible.

Copy and paste the result in your cartridge, as Pico-8 Map data tab

Basically you only need to locate your p8 cartridge, open it in your favorite text editor, and paste the result output by ImageToPico8. But first of all we’ll take some precaution and make some backups.
  1. In Pico-8 engine, press ctrl+S to save your cartridge —Let’s say it’s called “mygame”—, and close Pico-8 engine.
  2. Your game cartridge “mygame.p8” should be located in your Users folder. On Window the path where it’s located is: C:\Users\your_username\AppData\Roaming\pico-8\carts\mygame.p8
  3. Make a backup of this file, name it for example “mygame.p8.bak”. This will prevent you from breaking anything.
  4. Open “mygame.p8” in a text editor. Personally I use Notepad++. Keep this file opened.
  5. Now open the resulting file “MyImage_out.txt.p8” in the text editor as well. Select All and Copy the whole __map__ section.
  6. Then come back to your file “mygame.p8” (it should be in another tab of your text editor)
  7. Locate the __map__ section by searching "__map__" in this file (you can do this with “Ctrl+f” keyboard shortcut).
  8. Select the complete __map__ section you found including the 23 lines of data under this section, and Paste (Ctrl+V). This will replace the existing map data with the ones in the result of the image processing with ImageToPico8.
    *Note:* If there is no "__map__" section in your p8 file, Paste at the end of the file.
  9. In the text editor, save your "mygame.p8" (Ctrl+S).
  10. Finally run Pico-8 engine, type "load mygame.p8" and you will see that your map tab Map tab in pico8 now contains new data (small dark blue dots). Check the other tabs (code, sprites, sfx, music) to verify that everything is still there.
Congrats! Your __map__ data has been copied! The last thing you need to do is use these data, by direct access to the memory. How to do this is explained step by step below.

Display the image

Since your image data is not in the sprite tab, you cannot use the Pico-8 SPR function to draw your image. You will have to access directly to the memory location where it is stored.
You’ll need PEEK and POKE functions.
Blonde hair woman image for conversion into pico8 formatCode example in pico8 engine to display map data as image
An complete example of code to do this is:
bc={0,2,8,9} -- orange

function _init()
 cls()
 -- map: 0x2000 to 0x2fff (4096 bytes)
 --  expand (1 byte = 4 pixels (col 0..3 each))
 --  to display real colors (1 byte = 2 pixels)
 local addr_src=0x2000
 local addr_dst=0x6000
 local max_bytes=4096
 local i, val_src8, val_dst8

 for i=1,max_bytes do
  val_src8 = peek(addr_src)
  val_dst8 = addr_col(val_src8   ) + 16*addr_col(val_src8/4 )
  poke(addr_dst, val_dst8)
  val_dst8 = addr_col(val_src8/16) + 16*addr_col(val_src8/64)
  poke(addr_dst+1, val_dst8)
  addr_src+=1
  addr_dst+=2
 end
end

function _update()
end

function _draw()
end

function addr_col(val8)
 return bc[band(flr(val8),0x03)+1]
end
Why 0x2000 ? Because map data is located at this address in memory. Likewise, at 0x6000 you can directly access the display memory.
A complete explanation of the Pico-8 memory sections and addresses can be found here:
Pico-8 RAM - Memory sections and addresses
A more juicy example with wavy (cosine) animation and memory optimized in chunks.
Brown mountains image for conversion into pico8 formatCode example in pico8 engine to display and animate wave effect cosine
The code to perform the example above is:


bc={5,3,11,10} -- green
t=0

function addr_col(val8)
 return bc[band(flr(val8),0x03)+1]
end

function _init()
 -- cls()
 rectfill(0, 0,127, 88,bc[3])
 rectfill(0,89,127,108,bc[1])

 -- map: 0x2000 to 0x2fff (4096 bytes, truncated to 3496 bytes because of user_data size)
 --  expand (1 byte = 4 pixels (col 0..3 each))
 --  to pico8 user_data real colors (1 byte = 2 pixels)
 -- first copy map_data to user_data
 -- then user_data will be used to paste on display memory
 local addr_src=0x2000
 local addr_dst=0x4300
 local max_bytes=3496
 local i, val_src8, val_dst8

 for i=1,max_bytes do
  val_src8 = peek(addr_src)
  val_dst8 = addr_col(val_src8   ) + 16*addr_col(val_src8/4 )

  poke(addr_dst, val_dst8)
  val_dst8 = addr_col(val_src8/16) + 16*addr_col(val_src8/64)

  poke(addr_dst+1, val_dst8)

  addr_src+=1
  addr_dst+=2
 end
end

function _update()
 t+=1
end

function _draw()
 -- process each line to display memory
 local x,y,vo,vn
 local addr_tmp=0x1f80
 local addr_src=0x4300
 local addr_dst=0x6000
 for y=0,6991,64 do
  x=flr(2.5+2*sin(0.04*(t-0.6*y/64)))

  memcpy(addr_tmp,addr_src+y+flr(x/2),64)
  if x%2==1 then
   -- use temp address to shift only 1 pixel
   vn=peek(addr_tmp)
   for px=0,63 do
    vo=flr(vn/16)
    vn=peek(addr_tmp+px+1)
    vo+=16*band(vn,0x0f)
    poke(addr_tmp+px,vo)
   end
  end
  memcpy(addr_dst+y,addr_tmp,64-ceil(x/2))
 end

 print("x="..x,0,1,0)
 print("x="..x,2,1,0)
 print("x="..x,1,2,0)
 print("x="..x,1,0,0)
 print("x="..x,1,1,7)
end
Blue palette man converted to pico8 with ImgToPico8

Make a gif

You can use the flag -repeat to process the image several times and generate an animated GIF. For example, on a Windows Command Prompt, type:
ImgToP8 -orange -repeat32 "C:\my\path\myfile.jpg"
The example above will process the image 32 times (with the orange palette). Doing this will also create a animated GIF (made of 32 frames).
It is as well possible to generate multiple still images instead.
ImgToP8 -orange -repeat32 -still "C:\my\path\myfile.jpg"
...will generate 32 preview still images (PNG). That's useful if you post-process the image frames or if you prefer the preview images not to be animated.
Lively sunset picture converted to lowres pico8 with ImgToPico8 and animated

Animate pixels in Pico8 code

Since may 2021 (version 1.7) Image-To-Pico8 also has a solution to “fake the GIF effect” (explained above) but in P8 code. This new version now produces Pico8 lua code (in __lua__ section of the generated .p8 file) which integrates a small program to display the image on Pico8 screen. It includes a graphical jitter effect as a list of “changing pixels”: these are calculated by ImageToPico8 which will be finally drawn on Pico8 side. The rendering of these innerstate noise pixels simulates the animation of the image.
Integrated Pico8 code to animate imagesnow winter image post-process lively result (you need 500 to 2k tokens, depending on the definition you want to achieve)
This new feature takes “87+N” tokens: one token for each of the N “changing pixels” you want to draw and 87 tokens for the built-in program itself.
- You can adjust the number of tokens to allocate in your final p8 file (with the option -noisetokensNNNN : Allocate NNNN tokens of Pico-8 code) default is 2000 tokens = 1913 pixels.
- You can remove comments in Pico-8 code to reduce byte size (lines commented with “--”) with the option -nocommentsp8.
Example code generated by ImgToPico8 for a 128x85 image:
bc={5,15,6,7}
t=0

function _init()
 cls()
 pal(15,134,1)
 -- map: 0x2000 to 0x2fff (4096 bytes)
 --  expand (1 byte = 4 pixels (col 0..3 each))
 --  to display real colors (1 byte = 2 pixels)
 local addr_src=0x2000
 local addr_dst=0x6000
 local max_bytes=2720
 local i, val_src8, val_dst8

 for i=1,max_bytes do
  val_src8 = peek(addr_src)
  val_dst8 = addr_col(val_src8   ) + 16*addr_col(val_src8/4 )
  poke(addr_dst, val_dst8)
  val_dst8 = addr_col(val_src8/16) + 16*addr_col(val_src8/64)
  poke(addr_dst+1, val_dst8)
  addr_src+=1
  addr_dst+=2
 end
end

function _update()
 t+=1
end

function _draw()
 draw_pts()
end

function addr_col(val8)
 return bc[band(flr(val8),0x03)+1]
end

function draw_pts()
 -- list of 1913 pixel addresses (generated by ImgToPico8)
 local xy={-1787,1281,1150,117,1421,640,............,1,28,91,7,124,2,3,1}
 local v,p,c,d,i=0,0,1

 for i=1,#xy do
  d=xy[i]
  if d<0 then
   -- change pen, innerstate
   d=-d
   p+=1
   if p>7 then
    p=1
    c+=1
   end
  end
  v+=d
  if v>10879 then
   -- cycle through screen address
   v-=10880
  end
  pset(v%128,flr(v/128)%85,rndp(p,c))
 end
 rectfill(0,86,100,91,0)
 print("n_pts="..#xy.." cpu="..flr(stat(1)*100).."%".." t="..t,0,86,7)
end

function rndp(p,c)
 if rnd(8)<p then c+=1 end
 return bc[c]
end

About Pico-8

Please courteously note that Image-To-Pico8 (ImgToP8) is independent software, not affiliated with Pico-8 by Lexaloffle. Nevertheless Pico-8 is a marvelous engine to make, share and play tiny games and other computer programs (and even making music thanks to its built-in music tracker). Get Pico-8! - Full Pico-8 manual
See also