Surface
The Surface widget provides direct access to Hex1b's low-level rendering API, enabling arbitrary visualizations with multiple composited layers. Use it for custom graphics, game rendering, data visualization, or any UI that goes beyond standard widgets.
Basic Usage
Create a surface using Surface() with a layer builder that returns one or more layers:
dotnet runThe layer builder receives a SurfaceLayerContext that provides:
- Factory methods for creating layers (
Layer()) - Mouse position (
MouseX,MouseY) - Surface dimensions (
Width,Height) - Theme access for styled effects
Layer Types
Surfaces support three types of layers, composited bottom-up:
| Layer Type | Created With | Use Case |
|---|---|---|
| Source | s.Layer(ISurfaceSource) | Pre-rendered or externally managed surfaces |
| Draw | s.Layer(surface => { ... }) | Dynamic content drawn each frame |
| Computed | s.Layer(ctx => ...) | Effects that depend on layers below |
Draw Layers
Draw layers receive a fresh Surface each render. Use the indexer to set cells:
s.Layer(surface => {
surface[x, y] = new SurfaceCell(
character, // The character to display
foreground, // Foreground color (null = transparent)
background // Background color (null = transparent)
);
})2
3
4
5
6
7
Compositing Multiple Layers
Layers composite bottom-up. Later layers draw on top of earlier ones:
dotnet runComputed Layers
Computed layers calculate each cell based on layers below, enabling effects like fog of war, tinting, or shadows:
s.Layer(ctx => {
var below = ctx.GetBelow(); // Get the composited cell from layers below
// Apply a color tint by replacing the foreground
return below.WithForeground(Hex1bColor.Red);
})2
3
4
5
6
The ComputeContext provides:
X,Y- Current cell positionGetBelow()- Get the composited cell from all layers belowWidth,Height- Surface dimensions
Mouse Interaction
The layer context provides mouse position for interactive effects:
dotnet runEnable Mouse
Call .WithMouse() on the terminal builder to receive mouse position updates.
Sizing
By default, surfaces fill all available space. Control sizing with:
// Fixed size
ctx.Surface(...).Size(40, 20)
// Custom hints
ctx.Surface(...)
.Width(SizeHint.Fixed(40))
.Height(SizeHint.Content)2
3
4
5
6
7
SurfaceCell Properties
Each cell in a surface can have:
| Property | Type | Description |
|---|---|---|
Character | char | The character to display |
Foreground | Hex1bColor? | Text color (null = transparent) |
Background | Hex1bColor? | Background color (null = transparent) |
Sixel | TrackedObject<SixelData>? | Sixel graphics data |
Use Cases
Surfaces are ideal for:
| Scenario | Approach |
|---|---|
| Game rendering | Multiple layers: terrain, entities, UI, effects |
| Data visualization | Draw charts, graphs, heatmaps |
| Custom animations | Update layer content each frame |
| Image display | Use sixel graphics with CreateSixel() |
| Fog of war | Computed layer that masks based on visibility |
Sixel Graphics
Create sixel graphics for image display:
s.Layer(surface => {
// Create a pixel buffer (width x height in pixels)
var pixels = new SixelPixelBuffer(100, 50);
// Draw pixels
for (int py = 0; py < pixels.Height; py++)
for (int px = 0; px < pixels.Width; px++)
pixels[px, py] = Rgba32.FromRgb((byte)px, (byte)py, 128);
// Create tracked sixel and place it
var sixel = s.CreateSixel(pixels);
if (sixel != null)
{
surface[0, 0] = new SurfaceCell { Sixel = sixel };
}
})2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
Terminal Support
Sixel graphics require terminal support. Not all terminals support sixels.
Performance Tips
- Minimize computed layers - They run per-cell per-frame
- Use source layers for static content - Create once, reuse
- Cache surfaces when content doesn't change
- Keep layer count low - Each layer adds compositing overhead
Related
- Layout System - How sizing works
- Theming - Access colors via
s.Theme