TinyBit is a virtual console for creating and playing simple 2D games using Lua scripting. Games are stored as PNG cartridge files that steganographically embed both spritesheet assets and Lua script data.
- 128x128 pixel display with 4-bit RGBA color depth (RGBA4444)
- Lua scripting for game logic with built-in graphics, input, and audio APIs
- PNG-based cartridge format that embeds spritesheets and code in image LSBs
- Audio engine with ABC notation music and sound effects
- Built-in game selector for managing multiple games
- Cross-platform support (Windows, Linux, macOS)
sudo apt-get install libsdl2-dev libsdl2-image-devbrew install sdl2 sdl2_imageSDL2 development libraries are included in the repository.
- Clone the repository with submodules:
git clone --recursive https://github.com/your-repo/TinyBit.git
cd TinyBit- If you forgot
--recursive, initialize submodules:
git submodule init
git submodule update --recursive- Build with CMake:
mkdir build && cd build
cmake ..
cmake --build ../build/tinybit./build/tinybit games/flappy.tb.png./build/tinybit -c spritesheet.png script.lua cover.png output.tb.png- Screen Resolution: 128x128 pixels
- Color Depth: 4-bit RGBA (RGBA4444, 16 levels per channel)
- Cartridge Size: 200x230 pixels (PNG format)
- Audio: 22kHz 16-bit PCM, 2 channels (music + SFX)
- Target Frame Rate: 60 FPS
| TinyBit Button | Keyboard Key |
|---|---|
| A | A |
| B | B |
| UP | Arrow Up |
| DOWN | Arrow Down |
| LEFT | Arrow Left |
| RIGHT | Arrow Right |
| START | Enter |
| SELECT | Backspace |
cls()- Clear the displaysprite(sx, sy, sw, sh, dx, dy, dw, dh [, rotation])- Draw sprite with optional rotationduplicate(sx, sy, sw, sh, dx, dy, dw, dh [, rotation])- Copy display regionrect(x, y, w, h)- Draw rectangleoval(x, y, w, h)- Draw ovalline(x1, y1, x2, y2)- Draw line
poly_add(x, y)- Add vertex to polygonpoly_clear()- Clear polygon verticesdraw_polygon()- Draw filled polygon with current vertices
Colors are packed RGBA8888 values. Use the color helper functions to create them:
fill(color)- Set fill colorstroke(width, color)- Set stroke width and colortext(color)- Set text colorrgba(r, g, b, a)- Create color from RGBA (0-255 per channel)rgb(r, g, b)- Create color from RGB (alpha defaults to 255)hsb(h, s, b)- Create color from HSB (0-255 per component)hsba(h, s, b, a)- Create color from HSBA (0-255 per component)
cursor(x, y)- Set text cursor positionprint(text)- Print text at cursor position
btn(button)- Check if button is currently heldbtnp(button)- Check if button was just pressed this frame
Button constants: A, B, UP, DOWN, LEFT, RIGHT, START, SELECT
music(abc_string)- Play looping music from ABC notationsfx(abc_string)- Play one-shot sound effect from ABC notationsfx_active()- Check if a sound effect is currently playingbpm(beats_per_minute)- Set tempo
Waveform constants: SINE, SAW, SQUARE, NOISE
peek(address)- Read byte from user memorypoke(address, value)- Write byte to user memorycopy(dest, src, size)- Copy memory region
millis()- Get current frame time in millisecondsrandom(min, max)- Generate random integer in rangelog(message)- Print debug message to consolesleep(ms)- Delay execution
gamecount()- Get number of available gamesgamecover(index)- Load game cover for previewgameload(index)- Load and start game by index
TB_SCREEN_WIDTH(128),TB_SCREEN_HEIGHT(128)
Every TinyBit game must implement a _draw() function that gets called every frame:
function _draw()
cls()
if btnp(A) then
-- Handle A button press
end
sprite(0, 0, 16, 16, 50, 50, 16, 16)
end-- Simple bouncing ball
x, y = 64, 64
dx, dy = 1, 1
function _draw()
cls()
-- Update position
x = x + dx
y = y + dy
-- Bounce off walls
if x <= 0 or x >= 120 then dx = -dx end
if y <= 0 or y >= 120 then dy = -dy end
-- Draw ball
fill(rgb(255, 255, 255))
oval(x, y, 8, 8)
end- Create Assets: Design a 128x128 pixel spritesheet PNG
- Write Script: Create a Lua file with your game logic
- Design Cover: Create a 128x128 pixel cover image PNG
- Export Cartridge:
./build/tinybit -c spritesheet.png script.lua cover.png game.tb.png
The cartridge format embeds the spritesheet and script data in the least significant bits of the cartridge PNG, while the cover image is overlaid on top for visual identification.
The TinyBit library can be embedded in any C project. See src/tinybit/README.md for the full library documentation.
// Initialize system
void tinybit_init(struct TinyBitMemory* memory);
// Load cartridge data
bool tinybit_feed_cartridge(const uint8_t* cartridge_buffer, size_t bytes);
// Game lifecycle
bool tinybit_start();
void tinybit_loop();
bool tinybit_restart();
void tinybit_stop();
// Platform callbacks
void tinybit_render_cb(void (*render_func_ptr)());
void tinybit_poll_input_cb(void (*poll_input_func_ptr)());
void tinybit_get_ticks_ms_cb(int (*get_ticks_ms_func_ptr)());
void tinybit_audio_queue_cb(void (*audio_queue_func_ptr)());
void tinybit_log_cb(void (*log_func_ptr)(const char*));
void tinybit_gamecount_cb(int (*gamecount_func_ptr)());
void tinybit_gameload_cb(void (*gameload_func_ptr)(int index));TinyBit is designed to be a simple, educational game development platform. Contributions are welcome for:
- Bug fixes and optimizations
- Additional Lua API functions
- Cross-platform improvements
- Example games and tutorials
- Documentation improvements