A web-based tile and tilemap editor for the Cicada-16 fantasy console
Semitile is a focused, browser-based tool for creating graphics assets for the Cicada-16 system. It provides an accessible entry point for users to create tiles, palettes, and tilemaps without requiring any installation.
- Tile Editing - 8×8 pixel tile editor with pencil, fill, line, and rectangle tools
- 4bpp Planar Format - Native support for Cicada-16's 4-bit planar tile encoding
- RGB555 Palette System - 16 sub-palettes of 16 colors each (256 total colors)
- Tilemap Editor - Arrange tiles into scenes with support for all Cicada-16 BG_MODE sizes
- Screen Viewport Overlay - Visualize the 30×20 tile (240×160 pixel) screen area with draggable overlay and SCX0/SCY0 register values
- Tile Attributes - Configure palette, horizontal/vertical flip, and priority per tile
- Multi-Tile Management - Tile bank with add, delete, duplicate, and reorder operations
- Multi-Tilemap Support - Create and manage multiple tilemaps in a single project
- Export Formats - Binary, C headers, Assembly, and PNG exports for tiles, palettes, and tilemaps
- Project Management - Save/load projects to browser storage or export as JSON
- Undo/Redo - Full command history for all editing operations
- MVC Architecture - Clean separation of Models, Views, and Controllers
- React Components - Framework-agnostic Web Components with React wrappers
- WASM Core - Rust-based core for fast tile/palette operations
The easiest way to use Semitile is through the standalone web application:
-
Clone the repository:
git clone https://github.com/semikit-org/semitile.git cd semitile -
Build the WASM core:
cd web wasm-pack build --target web cd ..
-
Install UI dependencies:
cd ui npm install -
Start the development server:
# (in ui/ directory) npm run dev -
Open your browser: Navigate to
http://localhost:5173/src/standalone/(or the URL shown in the terminal)
The standalone application will load with a full tile and tilemap editor interface.
To create a production build of the standalone application:
cd ui
npm run buildThe built files will be in ui/dist/ and can be served from any static web server.
Semitile's UI components can be integrated into React applications using the provided React wrappers.
First, install Semitile UI and React dependencies in your project:
npm install react react-domThen, build the Semitile WASM module and copy it to your project:
# In the semitile repository
cd web
wasm-pack build --target web
# Copy the built WASM files to your project
cp -r pkg/ /path/to/your/project/public/wasm/Copy the Semitile UI source files to your project:
# Copy the UI source to your project
cp -r ui/src/ /path/to/your/project/src/semitile-ui/import React, { useEffect, useState } from "react";
import { initWasm } from "./semitile-ui/lib/wasm-loader";
import { TileBankModel, PaletteModel, EditorState } from "./semitile-ui/models";
import {
TileCanvasReact,
PaletteEditorReact,
ColorPickerReact,
ToolPanelReact,
useTileBankModel,
usePaletteModel,
useEditorState,
} from "./semitile-ui/react";
function TileEditorApp() {
const [models, setModels] = useState(null);
// Initialize WASM and create Models
useEffect(() => {
async function init() {
const wasm = await initWasm();
const tileBankModel = new TileBankModel();
const paletteModel = new PaletteModel(new wasm.WasmPalette());
const editorState = new EditorState();
setModels({ tileBankModel, paletteModel, editorState });
}
init();
}, []);
if (!models) return <div>Loading WASM...</div>;
return (
<EditorContent
tileBankModel={models.tileBankModel}
paletteModel={models.paletteModel}
editorState={models.editorState}
/>
);
}
function EditorContent({ tileBankModel, paletteModel, editorState }) {
// Hooks sync React state with Models (triggers re-renders on Model changes)
useTileBankModel(tileBankModel);
usePaletteModel(paletteModel);
useEditorState(editorState);
return (
<div
style={{
display: "grid",
gridTemplateColumns: "200px 1fr 200px",
gap: "20px",
}}
>
{/* Left Panel - Tools */}
<div>
<ToolPanelReact editorState={editorState} />
</div>
{/* Center Panel - Tile Canvas */}
<div>
<TileCanvasReact
tileBankModel={tileBankModel}
paletteModel={paletteModel}
editorState={editorState}
/>
</div>
{/* Right Panel - Palette */}
<div>
<PaletteEditorReact paletteModel={paletteModel} />
<ColorPickerReact paletteModel={paletteModel} />
</div>
</div>
);
}
export default TileEditorApp;All Semitile components are available as React wrappers:
TileCanvasReact- Tile editing canvasPaletteEditorReact- Palette grid viewerColorPickerReact- RGB555 color pickerToolPanelReact- Tool selection panelTileBankReact- Tile bank managementTilemapEditorReact- Tilemap editing canvasTilemapBankReact- Tilemap bank managementTileAttributesPanelReact- Tile attributes panel
Semitile provides hooks for syncing React state with Models:
useTileBankModel(model)- Syncs with TileBankModelusePaletteModel(model)- Syncs with PaletteModeluseEditorState(model)- Syncs with EditorStateuseTilemapModel(model)- Syncs with TilemapModel
See ui/src/react/README.md for complete React integration documentation.
Semitile uses a Model-View-Controller (MVC) architecture:
- Single source of truth for all application data
- Emit events when state changes
- Integrate with WASM for performance-critical operations
- Framework-agnostic (can be used in any JavaScript framework)
- Pure presentation components
- Listen to Model events and re-render when state changes
- Dispatch user interaction events to Controllers
- Framework-agnostic custom elements with Shadow DOM
- Can be used directly or wrapped for React/Vue/etc.
- Coordinate between Views and Models
- Handle user interaction events from Views
- Update Models based on user actions
- Contain tool logic, validation, and business rules
- High-performance tile/palette operations
- 4bpp planar encoding/decoding
- RGB555 color conversion
- Tilemap encoding
semitile/
├── web/ # WASM module (Rust → WebAssembly)
│ ├── src/
│ │ └── lib.rs # WASM bindings
│ └── Cargo.toml
├── ui/ # UI components (TypeScript)
│ ├── src/
│ │ ├── models/ # MODEL LAYER - Observable state
│ │ │ ├── TileBankModel.ts
│ │ │ ├── PaletteModel.ts
│ │ │ ├── EditorState.ts
│ │ │ └── TilemapModel.ts
│ │ ├── views/ # VIEW LAYER - Web Components
│ │ │ ├── TileCanvas/
│ │ │ ├── PaletteEditor/
│ │ │ ├── ColorPicker/
│ │ │ ├── ToolPanel/
│ │ │ └── TilemapEditor/
│ │ ├── controllers/ # CONTROLLER LAYER - Business logic
│ │ │ ├── TileEditorController.ts
│ │ │ ├── PaletteController.ts
│ │ │ └── TilemapController.ts
│ │ ├── standalone/ # Standalone web app
│ │ │ ├── index.html
│ │ │ ├── app.ts
│ │ │ └── styles.css
│ │ ├── react/ # React wrappers
│ │ │ ├── hooks/
│ │ │ ├── TileCanvasReact.tsx
│ │ │ └── ...
│ │ └── lib/ # Utilities
│ │ └── wasm-loader.ts
│ ├── package.json
│ └── vite.config.js
└── cicada-16/ # Cicada-16 hardware specs
└── HardwareSpec/
└── PPU_Architecture.md # PPU specification
- Rust (for WASM compilation) - Install from rustup.rs
- wasm-pack - Install with
cargo install wasm-pack - Node.js (v20.19+ or v22.12+) - For UI development
- npm or yarn - Package manager
cd web
wasm-pack build --target webThis generates the WASM module in web/pkg/ which the UI will import.
cd ui
npm install
npm run devOpen http://localhost:5173/src/standalone/ in your browser.
# Build WASM
cd web
wasm-pack build --target web --release
# Build UI
cd ../ui
npm run build# Test WASM core
cd web
cargo test
# Test UI (if tests are added)
cd ../ui
npm testSemitile is designed specifically for the Cicada-16 fantasy console and follows its hardware specifications:
- Tile Format: 8×8 pixels, 4bpp planar encoding (32 bytes per tile)
- Palette: RGB555 color format (16-bit per color)
- Sub-Palettes: 16 sub-palettes of 16 colors each (256 total)
- Tilemap Entry: 16-bit format with tile index, palette select, flips, and priority
- BG_MODE Sizes: 32×32, 64×32, 32×64, 64×64 tiles
See cicada-16/HardwareSpec/PPU_Architecture.md for complete PPU specifications.
Semitile can export assets in multiple formats:
- Tile Binary (.bin) - Raw 32-byte planar format per tile
- Palette Binary (.bin) - 512 bytes (256 colors × 2 bytes RGB555)
- Tilemap Binary (.bin) - 2 bytes per entry, little-endian
- Tile C Header (.h) - C array with planar tile data
- Palette C Header (.h) - C array with RGB555 palette data
- Tilemap C Header (.h) - C array with tilemap entries
- Tile Assembly (.asm) - Assembly data directives
- Palette Assembly (.asm) - Assembly data directives
- Tilemap Assembly (.asm) - Assembly data directives
- Tilemap PNG (.png) - Rendered tilemap as PNG image with configurable pixel size
- Project JSON (.json) - Complete project with all tiles, palettes, and tilemaps
All binary formats are compatible with direct DMA loading to Cicada-16 VRAM/CRAM.
Semitile requires modern browser features:
- ✅ Chrome/Edge 90+ - Full support
- ✅ Firefox 88+ - Full support
- ✅ Safari 14+ - Full support
- ❌ Internet Explorer - Not supported (Web Components not polyfillable)
Contributions are welcome! Please follow these guidelines:
- Fork the repository
- Create a feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
This project is licensed under the GNU General Public License v3.0 - see the LICENSE file for details.
Copyright (C) 2025 Connor Nolan (connor@cnolandev.com)
This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
- Cicada-16 - The fantasy console system that Semitile targets
- Semikit - Comprehensive development toolkit for Cicada-16
- Semikit Studio - Desktop IDE for Cicada-16 development (planned)
- Inspired by classic tile editors for retro game consoles
- Built with modern web technologies for accessibility and ease of use
- Designed to complement the Cicada-16 hardware architecture
For questions, issues, or feature requests:
- Open an issue on GitHub Issues
- Contact: connor@cnolandev.com