@reldens/tileset-to-tilemap
v0.5.0
Published
Reldens - Tileset to Tile Map
Downloads
278
Maintainers
Readme
Reldens - Tileset to Tile Map
A Node.js library that takes game tileset PNG images, detects individual game objects via pixel analysis, sends each cluster to AI providers for naming and layer assignment, and generates Tiled-compatible JSON files for use with the Reldens game platform.
Need some specific feature?
Request a feature here: https://www.reldens.com/features-request
Features
- Pixel-Level Cluster Detection: Analyzes tileset PNGs tile by tile using connected component labeling to group related tiles into clusters
- AI-Powered Naming: Sends each detected cluster to Claude, Gemini, or Ollama for automatic naming and layer assignment
- Multi-Provider Fallback: Tries AI providers sequentially (Ollama → Claude → Gemini), using the first valid result
- Layer Assignment: AI assigns each element's tiles to the correct Tiled layer types (below-player, collisions, over-player, collisions-over-player)
- Tiled JSON Output: Generates per-element Tiled-compatible JSON maps and a composite map with all elements
- Session Management: Saves and loads analysis sessions, supports renaming and incremental saves
- Tileset Merging: Combines multiple source tilesets into a single merged tileset with remapped elements
- Annotated Preview: Generates annotated PNG with colored overlays per element for visual review
- Express Integration:
TilesetAnalyzerServermounts all API routes onto any Express app with optional auth middleware - Zero env-var coupling: All configuration passed via constructor options with sensible defaults
Documentation
https://www.reldens.com/documentation/tileset-to-tilemap/
Installation
npm install @reldens/tileset-to-tilemapQuick Start
Embed in an Express app
const express = require('express');
const { Requirements, TilesetAnalyzerServer } = require('@reldens/tileset-to-tilemap');
async function start() {
let requirements = new Requirements({
ollamaHost: 'http://localhost:11434',
anthropicApiKey: process.env.ANTHROPIC_API_KEY,
geminiApiKey: process.env.GEMINI_API_KEY
});
let aiProviders = await requirements.resolveAiProviders();
let app = express();
let rootDir = '/path/to/generated-tile-map-elements';
// Serve uploaded input images and generated output files
app.use('/tileset-image', express.static(rootDir + '/input'));
app.use('/output', express.static(rootDir + '/output'));
let server = new TilesetAnalyzerServer(rootDir, {
publicDir: '/path/to/public',
aiProviders,
showAiControls: true,
claudeModel: 'claude-sonnet-4-6',
ollamaHost: 'http://localhost:11434'
});
server.registerRoutes(app);
app.listen(3000);
}
start();With auth middleware (e.g. admin integration)
server.registerRoutes(app, isAuthenticatedMiddleware);All routes automatically receive the middleware. Pass skipIndex: true in options to skip the GET / page route when embedding inside an existing app.
Detect clusters and generate files programmatically
const { AiAnalyzer, TilesetFilesBuilder, Helpers } = require('@reldens/tileset-to-tilemap');
const sharp = require('sharp');
let imageBuffer = await sharp('/path/to/tileset.png').png().toBuffer();
let meta = await sharp('/path/to/tileset.png').metadata();
let tileWidth = 32, tileHeight = 32, margin = 0, spacing = 0;
let analyzer = new AiAnalyzer({ ollamaHost: 'http://localhost:11434', skipAi: false });
let result = await analyzer.analyzeImage(
imageBuffer,
Helpers.calcTileColumns(meta.width, margin, spacing, tileWidth),
Helpers.calcTileRows(meta.height, margin, spacing, tileHeight),
tileWidth, tileHeight, margin, spacing,
providers, '#ffffff', null, null, null
);
// result = { elements: [{ name, type, approved, layers }], filteredTiles: [[row, col], ...] }
let builder = new TilesetFilesBuilder();
let files = await builder.build(rootDir, sessionId, outputDir, tilesets, tilesets, 'my-map', 'My Map');
// files = [{ name, downloadUrl, type: 'output'|'input' }]Configuration Options
All options are passed to constructors. The package reads no process.env directly. Claude and Gemini API keys are picked up from ANTHROPIC_API_KEY / GEMINI_API_KEY env vars by their respective SDKs.
TilesetAnalyzerServer(rootDir, options)
| Option | Default | Description |
|---|---|---|
| publicDir | 'public' | Path to static files directory |
| showAiControls | false | Show manual AI buttons in the web UI |
| aiProviders | [] | Array of active provider strings |
| skipAi | false | Disable AI analysis entirely |
| skipIndex | false | Skip GET / page route (for admin embed) |
All AI and detection options below are also accepted and passed through to route handlers.
AI Options (AiAnalyzer, AiProviderCaller, MultiAiAnalyzer)
| Option | Default | Description |
|---|---|---|
| claudeModel | 'claude-sonnet-4-6' | Claude model ID |
| claudeMaxTokens | 512 | Token budget for naming/verify |
| claudeMaxTokensDetection | 4096 | Token budget for detection |
| geminiModel | 'gemini-2.0-flash-preview-image-generation' | Gemini model ID |
| geminiMaxTokens | 512 | Token budget for naming/verify |
| geminiMaxTokensDetection | 4096 | Token budget for detection |
| ollamaHost | 'http://localhost:11434' | Ollama server URL |
| ollamaModel | 'qwen2.5vl:7b' | Default Ollama model |
| ollamaAvailableModels | '' | Comma-separated list of additional Ollama models |
| ollamaNumCtx | 8192 | Ollama context size |
| ollamaNumPredict | 2000 | Ollama max tokens |
| validatePass | false | Run a verify step after naming |
| skipAi | false | Skip all AI calls |
Detection Options (ClusterDetector, ClusterCropper)
| Option | Default | Description |
|---|---|---|
| minClusterTiles | 1 | Discard clusters smaller than this |
| clusterEmptyAlphaThreshold | 10 | Alpha below this = transparent |
| clusterColorDistance | 30 | RGB distance from bgColor = background |
| clusterVarianceThreshold | 600 | Exclude low-variance tiles |
| clusterMinTileFillPct | 10 | Min % of tile pixels that must be non-background |
| clusterSplitByGap | 1 | Split clusters at internal empty tile rows/cols |
| elementBorderColorDistance | 20 | Max color distance between adjacent tile edges for single-element classification |
How It Works
- Upload: Tileset PNG uploaded; Sharp reads pixel buffer
- Tile Scan: Each tile scanned for non-background pixel fill percentage and variance
- Connected Components:
image-jsruns connected component labeling on a binary pixel mask - Cluster Classification: Groups with uniform inter-tile border colors become elements; others stay as clusters
- AI Sub-Detection: Each cluster sent to AI to split into named sub-elements
- AI Naming + Layers: Each element cropped and sent to AI for a kebab-case name and layer assignments
- Multi-Provider Fallback: Providers tried in order (Ollama → Claude → Gemini); first valid result wins
- Output Generation: Per-element Tiled JSON, annotated PNG, composite map, and map-generator config written to
output/{sessionId}/
Output Files
All saved to generated-tile-map-elements/output/{sessionId}/:
session-editor-state.json— full tileset+element state (used by Load session)elements-config.json— same state formatted for readability{tileset-name}.png— copy of original tileset PNG{tileset-name}-{element-name}.json— per-element Tiled-format map{tileset-name}-annotated.png— tileset with colored element overlays and gridcomposite.json— all elements combined on one Tiled mapmap-generator-config.json— Reldens map generator config
Input files stored in generated-tile-map-elements/input/{sessionId}/.
Dependencies
- sharp: Pixel-level image processing and cropping
- image-js: Connected component labeling for cluster detection
- archiver: ZIP download of session output files
- multer: Multipart file upload handling
- @anthropic-ai/sdk: Claude AI provider
- @google/genai: Gemini AI provider
- @reldens/utils: Logger, Shortcuts (
sc) - @reldens/server-utils: FileHandler
Related Packages
- @reldens/tile-map-optimizer: Optimize Tiled JSON maps by removing unused tiles and merging tilesets
- @reldens/tile-map-generator: Generate random tile maps for Reldens
- Reldens: MMORPG platform that uses these generated maps

