tic80-image-tools
v0.0.1
Published
Export Tic-80 tiles and sprites to PNG images, and import image sheets back to cartridges.
Maintainers
Readme
Tic-80 Image Tools
Export and import Tic-80 tiles and sprites to/from PNG images.
👉 Read QUICKSTART.md for a 2-minute walkthrough
Features
- Bidirectional - Export tiles and sprites to PNG and import them back
- Multi-bank support - Handles all 8 TIC-80 memory banks automatically
- Multi-palette - Exports all palettes per bank separately
- Tile and sprite sheets - Separate full_tileset.png and full_spritesheet.png
- Interactive CLI - Just run and follow prompts
- Auto-detection - Finds .lua files in current directory
- Color quantization - Automatically matches closest palette colors
- Safe imports - Creates automatic backups before modifying cartridges
- Bank targeting - Import to specific bank and palette
- Standalone - No external tools needed
- Fast - Converts tiles and sprites in milliseconds
Installation
Global (for any project)
npm install -g tic80-image-toolsLocal (for a specific project)
npm install --save-dev tic80-image-toolsFrom GitHub
npm install --save-dev github:RafaelLVX/tic80-image-toolsUsage
Quick Start
Just run the CLI and follow the interactive prompts:
# If installed globally
tic80-img
# If installed locally in your project
npx tic80-imgThen:
- Choose operation: Export or Import
- For Export:
- Choose your .lua cartridge (or enter a relative path like
src/gamename.lua) - Choose format: Indexed PNG (only format that can be reintroduced to TIC-80)
- Choose scale: 1x (only scale that can be reintroduced to TIC-80)
- Done! PNGs appear in
tileset_[cartridgename]/
- Choose your .lua cartridge (or enter a relative path like
- For Import:
- Enter path to your edited sheet.png
- Choose target cartridge to update
- Confirm the operation (a backup is created automatically)
- Done! Tiles are written back to the cartridge
Relative Paths
When the tool asks for a cartridge path, use relative paths from your current directory:
Example: If your project structure is:
my-game/
├── src/
│ ├── gamename.lua
│ ├── game2.lua
├── graphics/
└── tileset_tools/ (or just run from my-game/)Run from my-game/ and enter: src/gamename.lua or src\gamename.lua
The tool resolves the path relative to where you run the command from, making it natural to integrate into your game development workflow.
Output
tileset_gamename/
├── palette.png (color palette reference - 2×8 grid)
├── sheet.png (all tiles combined)
├── tile_001.png
├── tile_016.png
├── tile_017.png
└── ... (tiles)Example Workflows
Export Workflow
$ npx tic80-imgThen just follow the interactive prompts:
🎮 Tic-80 Image Tools
? What do you want to do? Export all tiles and sprites to PNG
? Enter path to .lua cartridge (relative to current directory): gamename.lua
📖 Parsing cartridge...
Bank 0: 19 tiles, 8 sprites (2 palettes)
🎨 Converting tiles and sprites (indexed PNG)...
...................
📋 Creating tile sheet...
✓ tiles_sheet.png
📋 Creating sprite sheet...
✓ sprites_sheet.png
🎨 Creating palette reference...
✓ palette.png
✅ Export complete!
Cartridge: gamename
Output: D:\my-game\gamename_image_export/bank_0/palette_0/Multi-bank cartridges automatically detect and extract all banks:
✅ Export complete!
Cartridge: gamename
Bank 0: 19 tiles, 8 sprites (2 palettes)
Bank 1: 0 tiles, 0 sprites
Bank 7: 5 tiles, 3 sprites (1 palette)
Output: D:\my-game\gamename_image_export/Import Workflow
After editing your tiles or sprites in an image editor:
🎮 Tic-80 Image Tools
? What do you want to do? Import tiles to cartridge
? Path to full tileset (PNG, max. 256 8x8-pixel tiles) gamename_image_export/bank_0/palette_0/full_tileset.png
? Enter cartridge path: gamename.lua
? Select target bank (0-7): 0
? Select target palette (0-1): 0
⚠️ Warning: This will overwrite tiles in Bank 0, Palette 0.
A backup will be created automatically (.backup file).
? Proceed with import? Yes
📥 Importing tiles to cartridge...
Source: full_tileset.png
Target: gamename.lua (Bank 0, Palette 0)
🔍 Extracting tiles from sheet...
Found 19 tiles
🔄 Converting to TIC-80 format and quantizing colors...
✅ Import complete!
Imported: 19 tiles
Updated: gamename.lua
Backup: gamename.lua.backupKey features of import:
- Correct tile numbering: Follows TIC-80 grid layout (16 tiles per row) automatically
- Automatic color quantization: Any colors in your edited PNG are matched to the closest colors in the cartridge's palette (though using exact palette colors are recommended)
- Safe operation: Always creates a
.backupfile before modifying the cartridge - Flexible input: Works with sheets at any scale (1x, 2x, 4x, 8x) - automatically downsampled to 8×8 tiles (though 1x is recommended)
Tile Grid Layout
TIC-80 organizes tiles in a 16×16 grid (0-255). When you import a sheet, tiles are positioned based on their location:
Standard 16-wide sheet (full width):
Row 0: tiles 0-15
Row 1: tiles 16-31
Row 2: tiles 32-47
...Sheet requirements:
- Width: Must be exactly 128px, 256px, 512px, or 1024px (16 tiles wide at 1x, 2x, 4x, or 8x scale)
- Height: Can be any multiple of 8px, up to 16 rows (fewer than 16 rows is fine if you don't need all 256 tiles)
- Scaling: The tool samples the center pixel of each scaled tile to convert back to 8×8
Example: A 256×128 PNG (16-wide at 2x scale, 8 rows) imports tiles 0-127.
The tool automatically calculates tile IDs based on position:
tileId = (row * 16) + colImportant: The tool assumes your sheet starts at tile 0. If you need a different starting tile, extract your cartridge with the export function first, and it will match the original layout automatically.
Color Quantization
When importing tiles back to the cartridge, the tool automatically matches each pixel color to the closest color in the cartridge's 16-color palette.
Algorithm: Euclidean distance in RGB color space
distance = sqrt((R1-R2)² + (G1-G2)² + (B1-B2)²)How it works:
- Read each pixel from your edited sheet.png
- Calculate color distance to all 16 palette colors
- Select the palette color with minimum distance
- Use that palette index (0-15) in the tile data
Best practices:
- Use the
palette.pngreference to see exact cartridge colors. - You CAN edit at larger scales (2x, 4x) if you find it easier to draw, but the tool will automatically downsample the full set 1x when importing.
- Colors slightly off-palette will snap to nearest match.
Format Options
Indexed PNG (Default)
- Color palette: Exact 16-color palette from cartridge embedded in PNG (8-bit) (theoretically the only format that can be safely reintroduced to cartridge, though the tool will attempt to match colors to existing palettes).
- Important: Image editors will typically convert indexed PNG to RGB for editing, discarding palette information. Be careful when adding new colors to try match existing color palettes as best you can.
RGB PNG
- Color palette: Full RGB color space (not indexed, reducing TIC-80 compatibility).
- Use case: Viewing, sharing, or external use.
Output Location
By default, the tileset folder is created next to your cartridge:
gamename_image_export/
├── bank_0/
│ ├── palette_0/ (tiles, sprites, palette.png)
│ └── palette_1/ (if available)
├── bank_1/
│ └── palette_0/
...
└── bank_7/
└── palette_0/What It Does
Cartridge Parsing
- Extracts the
<PALETTE>section (16 RGB colors) - Extracts the
<TILES>sections (8×8 pixel tiles, or up-scaled on demand) - Extracts the
<SPRITES>sections (8×8 pixel sprites, or up-scaled on demand)
Image Generation
- Converts color indices to RGB values
- Renders pixel data to PNG format
- Upscales for better visibility or shareability (optional)
- Creates combined full tileset and spritesheet
- Exports image with palette color reference
Requirements
- Node.js 14.0.0 or higher
- npm or yarn
Limitations
This tool currently only supports a limited set of TIC-80 cartridges:
- Lua files (gamename.lua)
- Saved from PRO version of TIC-80
- 4 bits per pixel
The tool might work to an extent with non-Lua cartridges depending on how they're structured, but this was never tested. It will not work on .tic files at all.
The tool was never tested with 2 or 1 bit per pixel, and it's not expected to work in those cases.
How It Works
Tic-80 stores tiles as hex-encoded data:
- Each tile = 64 hex characters
- Each character = one pixel (0-15 = color index)
- Palette maps color indices to RGB values
Example:
-- <PALETTE>
-- 000:1a1c2c5d275db13e53... (16 colors, 6 hex chars each)
-- </PALETTE>
-- <TILES>
-- 020:ccccccccffffffffffffffffffffffffffffffffffffffffffffffffffffffff
-- ↑ tile 20: 'c' = color 12, 'f' = color 15
-- </TILES>The tool:
- Parses hex strings to color indices
- Looks up each color in the palette
- Renders to PNG with proper RGB values
Troubleshooting
"No palette found"
- Make sure you're using a valid Tic-80 .lua cartridge file
"No TILES section"
- Your cartridge has an empty tileset
"File not found"
- Check the path and make sure the file exists
License
MIT - Use freely in your projects.
Related
- Tic-80 - Tiny computer game engine
- Sharp - Image processing
- Inquirer.js - Interactive CLI prompts
Future Roadmap (?)
- [ ] Palette editing
- [ ] Support for 2/1 bits per pixel
- [ ] Support for other programming languages (non-Lua)
- [ ] Map export/import
Support
For issues or questions:
- Check the Tic-80 documentation
- Review example cartridges
- File an issue on GitHub
