Skip to content

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:

csharp

The 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 TypeCreated WithUse Case
Sources.Layer(ISurfaceSource)Pre-rendered or externally managed surfaces
Draws.Layer(surface => { ... })Dynamic content drawn each frame
Computeds.Layer(ctx => ...)Effects that depend on layers below

Draw Layers

Draw layers receive a fresh Surface each render. Use the indexer to set cells:

csharp
s.Layer(surface => {
    surface[x, y] = new SurfaceCell(
        character,      // The character to display
        foreground,     // Foreground color (null = transparent)
        background      // Background color (null = transparent)
    );
})

Compositing Multiple Layers

Layers composite bottom-up. Later layers draw on top of earlier ones:

csharp

Computed Layers

Computed layers calculate each cell based on layers below, enabling effects like fog of war, tinting, or shadows:

csharp
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);
})

The ComputeContext provides:

  • X, Y - Current cell position
  • GetBelow() - Get the composited cell from all layers below
  • Width, Height - Surface dimensions

Mouse Interaction

The layer context provides mouse position for interactive effects:

csharp

Enable Mouse

Call .WithMouse() on the terminal builder to receive mouse position updates.

Sizing

By default, surfaces fill all available space. Control sizing with:

csharp
// Fixed size
ctx.Surface(...).Size(40, 20)

// Custom hints
ctx.Surface(...)
   .Width(SizeHint.Fixed(40))
   .Height(SizeHint.Content)

SurfaceCell Properties

Each cell in a surface can have:

PropertyTypeDescription
CharactercharThe character to display
ForegroundHex1bColor?Text color (null = transparent)
BackgroundHex1bColor?Background color (null = transparent)
SixelTrackedObject<SixelData>?Sixel graphics data

Use Cases

Surfaces are ideal for:

ScenarioApproach
Game renderingMultiple layers: terrain, entities, UI, effects
Data visualizationDraw charts, graphs, heatmaps
Custom animationsUpdate layer content each frame
Image displayUse sixel graphics with CreateSixel()
Fog of warComputed layer that masks based on visibility

Sixel Graphics

Create sixel graphics for image display:

csharp
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 };
    }
})

Terminal Support

Sixel graphics require terminal support. Not all terminals support sixels.

Performance Tips

  1. Minimize computed layers - They run per-cell per-frame
  2. Use source layers for static content - Create once, reuse
  3. Cache surfaces when content doesn't change
  4. Keep layer count low - Each layer adds compositing overhead

Released under the MIT License.