Layer System ​
The layer system provides z-order rendering, allowing you to organize your scene into multiple layers that can be shown, hidden, and rendered in a specific order.
Overview ​
Layers let you separate different parts of your UI/game into independent render buffers. Common use cases:
- Background layer: terrain, floor tiles
- Entities layer: player, enemies, NPCs
- UI layer: menus, HUD, dialogs
Basic Usage ​
const renderer = new Renderer(target);
renderer
.layer("background")
.fill(0, 0, 80, 24, ".", "gray")
.layer("entities")
.setChar(40, 12, "@", "yellow")
.layer("ui")
.box(0, 0, 20, 5, { style: "single", fg: "white" })
.render();API Reference ​
layer(name: string): this ​
Switch to or create a named layer. All subsequent drawing operations affect this layer until you switch to another.
Parameters:
name: Layer name (string)
Returns: The renderer instance for chaining
Example:
renderer
.layer("background")
.fill(0, 0, 80, 24, " ", null, "black")
.layer("foreground")
.setChar(10, 10, "@");layerOrder(order: string[]): this ​
Set the rendering order of layers. Layers are rendered from first to last, so the last layer in the array appears on top.
Parameters:
order: Array of layer names in render order
Returns: The renderer instance for chaining
Example:
renderer.layerOrder(["background", "entities", "effects", "ui"]).render();Note: Layers not in the order array won't be rendered. The default order is ['main'].
hideLayer(name: string): this ​
Hide a layer from rendering without clearing its contents.
Parameters:
name: Layer name
Returns: The renderer instance for chaining
Example:
renderer
.hideLayer("ui") // Temporarily hide UI
.render();showLayer(name: string): this ​
Show a previously hidden layer.
Parameters:
name: Layer name
Returns: The renderer instance for chaining
Example:
renderer
.showLayer("ui") // Bring UI back
.render();clearLayer(name: string): this ​
Clear all content from a specific layer without affecting other layers.
Parameters:
name: Layer name
Returns: The renderer instance for chaining
Example:
renderer
.clearLayer("temporary") // Clear temp layer only
.render();Patterns & Best Practices ​
Game Loop with Layers ​
const renderer = new Renderer(canvasTarget);
// Setup layer order once
renderer.layerOrder(["background", "entities", "ui"]);
function gameLoop() {
// Clear only what changes
renderer.clearLayer("entities").clearLayer("ui");
// Redraw entities
renderer.layer("entities").setChar(player.x, player.y, "@", "yellow");
enemies.forEach((enemy) => {
renderer.setChar(enemy.x, enemy.y, "E", "red");
});
// Draw UI
renderer.layer("ui").drawText(2, 1, `HP: ${player.hp}`, { fg: "green" });
// Render all layers
renderer.render();
requestAnimationFrame(gameLoop);
}Toggle UI Visibility ​
let uiVisible = true;
function toggleUI() {
if (uiVisible) {
renderer.hideLayer("ui");
} else {
renderer.showLayer("ui");
}
uiVisible = !uiVisible;
renderer.render();
}Dynamic Layer Management ​
// Add a temporary effect layer
renderer.layer("effects").setChar(x, y, "*", "yellow");
// Existing layers array
const currentOrder = ["background", "entities", "ui"];
// Insert effects layer before UI
renderer.layerOrder(["background", "entities", "effects", "ui"]);
// Later, remove effect
renderer.clearLayer("effects");
renderer.layerOrder(["background", "entities", "ui"]);Performance Notes ​
- Layer compositing is fast: Layers are composited into a single buffer before rendering
- Don't clear what doesn't change: Static layers (like background) can be drawn once and left alone
- Layer count doesn't matter much: Having 10 layers with 100 cells each is roughly the same cost as 1 layer with 1000 cells
- Rendering order matters: Only visible layers in
layerOrderare composited
Common Patterns ​
Background That Never Changes ​
// Draw once
renderer.layer("background").fill(0, 0, 80, 24, ".", "gray");
// In game loop, don't clear or redraw background
function update() {
renderer
.clearLayer("entities") // Only clear what moves
.layer("entities")
.setChar(player.x, player.y, "@");
renderer.render(); // Background persists
}Modal Dialog Over Game ​
// Game running normally
renderer.layerOrder(["background", "entities", "ui"]);
// Show modal
renderer
.layer("modal")
.box(20, 8, 40, 10, { style: "double", fill: true, bg: "black" })
.centerText(10, "Are you sure?", { fg: "yellow" })
.layerOrder(["background", "entities", "ui", "modal"])
.render();
// Hide modal later
renderer
.clearLayer("modal")
.layerOrder(["background", "entities", "ui"])
.render();Z-Fighting (when two layers overlap) ​
If two layers have content at the same position, the last layer in the render order wins:
renderer
.layer("layer1")
.setChar(10, 10, "A", "red")
.layer("layer2")
.setChar(10, 10, "B", "blue")
.layerOrder(["layer1", "layer2"])
.render();
// Result: 'B' (blue) is visible, because layer2 is lastArchitecture ​
The layer system uses a LayerManager internally to handle:
- Layer creation and management
- Visibility tracking
- Render order enforcement
- Buffer compositing
Each layer maintains its own Map<string, Cell> buffer, and during render, all visible layers are composited into a single output buffer in the specified order.
Related: Renderer API | Camera System
