Skip to content

03 05 Config Display Layouts

evets17 edited this page Apr 9, 2026 · 6 revisions

03 05 Config Display Layouts

This page covers layout definitions used by [display].

If you have not read the display overview yet, start with:

What is a layout

A layout is a named screen arrangement under:

  • display.layouts.<layout_name>

Each layout contains an ordered list of elements that Sprint Boost draws in order.

Layout-level optional fields:

  • background_color (string, optional)
    • #RRGGBB or #RRGGBBAA.
    • If unset, uses global display.background_color.
  • font_path (string, optional)
    • Default font for type = "text" and type = "leaderboard" in this layout.
    • If unset, Sprint Boost falls back to display.font_path.
    • Useful when one layout should have its own look without repeating the same font on every text element.
  • enable_game_border_background_color (bool, default false)
    • When true, layout background follows game border color when available.
    • Falls back to layout/global background color if game color is unavailable.

Basic layout example

[display]
default_layout = "fullscreen"

[display.layouts.fullscreen]

[[display.layouts.fullscreen.elements]]
type = "game"
x = 0
y = 0
width = "100%"
height = "100%"
aspect = "force_4x3"

Draw order (important)

Elements render in the order they are listed.

Common pattern:

  1. Draw type = "game" first
  2. Draw overlays/images/text after that

This makes overlays appear on top of gameplay.

Multi-element layout example

This example shows a common layered layout:

  • game viewport
  • themed image panel
  • live text value
[display]
default_layout = "with_panel"

[display.layouts.with_panel]

[[display.layouts.with_panel.elements]]
type = "game"
x = { anchor = "right", offset = 0 }
y = 0
width = "75%"
height = "100%"
aspect = "force_4x3"
alpha = 1.0

[[display.layouts.with_panel.elements]]
type = "image"
source = "{gv.assets_images}/status_panel.png"
fallback = "none"
x = { anchor = "left", offset = 0 }
y = 0
width = "25%"
height = "100%"
scaling = "none"
alpha = 1.0

[[display.layouts.with_panel.elements]]
type = "text"
text = "{gi.score}"
x = 20
y = 620
width = 260
height = 70
color = "#DEEF00"
align = "center"
size = 42
alpha = 1.0

Because draw order is top-to-bottom, this renders as:

  1. game first
  2. panel image on top
  3. text on top of both

Common element types

Shared properties for all element types:

  • x, y (position)
  • width, height (size)
  • alpha (opacity, 0.0..1.0)

See Positioning guide and Sizing guide below for valid formats.

type = "game"

Draws the game view.

Properties:

  • aspect (string, default maintain)
    • Controls how the game framebuffer fits inside the element box.
    • Common values: maintain, stretch, force_4x3, force_16x9, integer_scale.
  • border (table, optional)
    • Only valid on type = "game".
    • If used under non-game element types, Sprint Boost warns and ignores it.

How aspect works:

  • The game element gives Sprint Boost a box to draw into.
  • aspect decides how the game picture fits inside that box.
  • In practice, this controls whether the picture keeps its normal shape, stretches, or snaps to cleaner whole-number scaling.

Aspect modes:

  • maintain

    • Keeps the game’s native aspect ratio.
    • Sprint Boost scales the game as large as it can inside the element box without distorting it.
    • This is the safest general-purpose choice when you want the game to keep its original shape.
    • Use this when you want the game to look natural but do not need to force it into a specific presentation ratio.
  • stretch

    • Fills the entire element box exactly.
    • Does not preserve the original aspect ratio.
    • This can make the game appear wider, taller, or generally distorted if the box shape does not match the game shape.
    • Use this only when full-box coverage matters more than correct proportions.
  • force_4x3

    • Forces the visible game result to fit a 4:3 shape inside the element box.
    • This is the most common choice for Intellivision layouts.
    • If your layout box is larger or wider than 4:3, Sprint Boost centers a 4:3 game region inside it.
    • Use this when you want a predictable classic-TV style game presentation.
  • force_16x9

    • Forces the visible game result to fit a 16:9 shape inside the element box.
    • This is useful for intentionally widescreen presentation styles.
    • It is less common for traditional Intellivision-style display, but can work for stylized or modern layouts.
    • Use this when your goal is a full widescreen presentation rather than classic proportions.
  • integer_scale

    • Scales the game only in whole-number steps.
    • This usually gives a cleaner, more pixel-stable look because Sprint Boost avoids fractional scaling.
    • It may leave more unused space inside the element box because it will not stretch to fill every last pixel.
    • Use this when sharpness and pixel consistency matter more than filling the available area.

Quick guidance:

  • Use force_4x3 for most standard gameplay layouts.
  • Use integer_scale when you want the crispest, most pixel-clean result.
  • Use maintain when you want to preserve the natural game shape without forcing a specific presentation ratio.
  • Use stretch only when distortion is acceptable.
  • Use force_16x9 only when the layout is intentionally designed around widescreen presentation.

Practical examples:

  • A centered main gameplay layout usually wants force_4x3.
  • A pixel-focused layout such as a native-scale or sharp HUD view often wants integer_scale.
  • A full-screen themed background layout that must fill a widescreen area may choose force_16x9 or stretch, depending on whether distortion is acceptable.

Border table fields:

  • enabled (bool, default false)
  • mode (string, default fit; currently supported: fit)
  • base_size (pixels or percent string, default 0)
  • left_adjust, right_adjust, top_adjust, bottom_adjust (i32, default 0)
  • color (string, default display_background)
    • game, display_background, or hex (#RRGGBB / #RRGGBBAA)
  • center_on (string, default game_frame)
    • game_frame or bordered_frame

What the border is for:

  • It adds a colored frame around the gameplay inside the game element box.
  • This is useful when a layout would otherwise leave awkward empty space around the game.
  • It also helps TV-frame and bezel layouts look more intentional because the gameplay can sit inside a matching surround instead of a hard edge.

How sizing works:

  • base_size sets the starting thickness for all four sides.
  • left_adjust, right_adjust, top_adjust, and bottom_adjust let you fine-tune each side after that.
  • A percent base_size uses the game element size, so border thickness scales with the layout.

How centering works:

  • center_on = "game_frame" keeps the gameplay itself centered, then adds the border around it.
  • center_on = "bordered_frame" centers the full bordered result instead.
  • If left and right border sizes are different, these two settings can produce different visual placement.

Border color choices:

  • color = "game" follows the current game border color.
  • color = "display_background" matches the layout background.
  • A hex color gives you a fixed custom frame color.

Related layout option:

  • enable_game_border_background_color = true is layout-wide. It changes the layout background to follow the game border color.
  • That is separate from the game element border itself. You can use either one or both.

Example:

[[display.layouts.with_border.elements]]
type = "game"
x = "center"
y = "center"
width = 960
height = 720
aspect = "force_4x3"

[display.layouts.with_border.elements.border]
enabled = true
mode = "fit"
base_size = "7%"
color = "game"
center_on = "game_frame"
left_adjust = 0
right_adjust = 0
top_adjust = 0
bottom_adjust = 0

type = "image"

Draws an image file (overlay, frame, guide art, etc.).

Properties:

  • source (string, required)
    • Primary image path.
    • Supports substitutions like {game_folder}, {game_file}, {game_file_ext}, {gv.<key>}.
  • fallback (string, optional)
    • Used if source cannot be loaded.
    • Use "none" to disable fallback.
  • scaling (string, default fit)
    • none, fit, fill, stretch.
  • blend (string, default normal)
    • normal, multiply, add, overlay.
  • trim_transparent_edges (bool or table, default disabled)
    • true is shorthand for { alpha_threshold = 0 }.
    • Removes fully transparent outer rows and columns before drawing.
    • alpha_threshold > 0 is allowed, but lossy.
  • transparent_region_optimization (bool or table, default disabled)
    • true is shorthand for { mode = "transparent_hole_split", min_hole_width = 192, min_hole_height = 128, alpha_threshold = 0 }.
    • Current supported mode: transparent_hole_split.
    • Detects one large interior transparent rectangle and splits the image into 2 to 4 bounded pieces that reuse the same texture.
    • min_hole_width / min_hole_height default to 192 / 128.
    • alpha_threshold > 0 is allowed, but lossy.

Supported image formats:

  • PNG
  • JPEG / JPG

Practical recommendation:

  • Prefer PNG for overlays/panels when possible.

Transparent optimization notes:

  • Both features are opt-in; existing image behavior is unchanged unless enabled.
  • If both are enabled, Sprint Boost trims transparent edges first and then evaluates transparent-hole splitting on the trimmed bounds.
  • If no qualifying interior hole is found, Sprint Boost falls back to the normal single-image draw.
  • Layouts using either optimization automatically bypass display.cache_static_layer for that active base layout.

Example:

[[display.layouts.old_tv.elements]]
type = "image"
source = "art/old_tv_bezel.png"
x = 0
y = 0
width = "100%"
height = "100%"
scaling = "stretch"
trim_transparent_edges = true
transparent_region_optimization = { mode = "transparent_hole_split", min_hole_width = 192, min_hole_height = 128, alpha_threshold = 0 }

type = "image_viewer"

Draws an interactive image viewer inside the layout.

This is useful for:

  • manuals
  • reference pages
  • move lists
  • map sheets
  • artwork collections you want to browse at runtime

Common idea:

  • type = "image" is for one fixed image
  • type = "image_viewer" is for a browsable set of images

Properties:

  • id (string, optional)
    • Needed when you want to target this viewer from default_focus_element or image-viewer hotkey commands.
  • source_folder (string, optional)
    • Loads supported image files from a folder.
    • Supports substitutions such as {gv.manual_path} or {game_folder}.
  • source_list (array of strings, default [])
    • Adds explicit image paths to the viewer.
    • Useful when you want to include a shared cover page or a file outside the normal folder.
  • source_filters (array of tables, default [])
    • Filters files found through source_folder.
    • Each filter has:
      • op = include or exclude
      • pattern = basename wildcard match using * and ?
  • source_order (string, default alphabetical)
    • alphabetical, natural, random
  • scaling (string, default fit)
    • none, fit, fill, stretch
  • sampling (string, default linear)
    • linear or nearest
  • background_color (string, optional)
    • Fills the viewer box behind the image.
  • wrap_navigation (bool, default true)
    • If true, moving past the last image wraps to the first, and vice versa.
  • input_when_focused (string, default suppress_handled)
    • suppress_handled, suppress_all, passthrough
  • zoom (bool or table, default enabled)
    • true is shorthand for enabled.
    • Table fields:
      • enabled
      • min
      • max
      • step
  • pan (bool or table, default enabled)
    • true is shorthand for enabled.
    • Table field:
      • enabled
  • focus_indicator (bool or table, default enabled)
    • true is shorthand for enabled.
    • Table fields:
      • enabled
      • color
      • thickness

How source loading works:

  • source_folder and source_list are additive.
  • Files from source_folder can be filtered by source_filters.
  • source_list is always explicit and is not filtered by source_filters.

How source_filters works:

  • If you use one or more include filters, a file must match at least one include filter to be kept.
  • Any exclude filter removes a file even if it matched an include filter.
  • Matching uses the file name only, not the full path.

Practical use cases:

  • source_filters = [{ op = "exclude", pattern = "_*" }]
    • skip helper files whose names start with _
  • source_filters = [{ op = "include", pattern = "{game_file}*" }]
    • include only manual pages for the current game

Supported image formats:

  • PNG
  • JPEG / JPG

Practical recommendations:

  • source_order = "natural" is usually the best first choice for numbered manual pages.
  • sampling = "nearest" is a good first thing to try if text looks too soft.
  • Use background_color = "#000000" when you want the viewer area to feel clean and self-contained.
  • Give the viewer an id if you plan to drive it with hotkeys.

Interaction notes:

  • The viewer keeps its current page and zoom state during the current runtime session.
  • Focus is runtime state. If the viewer is hidden, focus is cleared.
  • Viewer commands such as next/previous page, pan, zoom, and reset view target a specific viewer id.

Example:

[display.layouts.manual_overlay]
background_color = "#101820"
pause_on_enter = { enabled = true, resume_on_exit = false, hide_pause_menu = true }
default_focus_element = "pages"

[[display.layouts.manual_overlay.elements]]
type = "image_viewer"
id = "pages"
x = { anchor = "center", offset = 0 }
y = { anchor = "center", offset = 0 }
width = "90%"
height = "85%"
source_folder = "manuals/sprint"
source_list = ["manuals/shared/cover.png"]
source_filters = [
	{ op = "include", pattern = "{game_file}*" },
	{ op = "exclude", pattern = "_*" }
]
source_order = "natural"
sampling = "nearest"
background_color = "#000000"
wrap_navigation = true
input_when_focused = "suppress_handled"
zoom = { enabled = true, min = 1.0, max = 4.0, step = 0.25 }
pan = true
focus_indicator = { enabled = true, color = "#FFD54A", thickness = 3 }

type = "text"

Draws text values (for example game info or tracked stats).

Font fallback order:

  1. display.layouts.<layout>.elements[].font_path
  2. display.layouts.<layout>.font_path
  3. display.font_path
  4. Sprint Boost built-in default font search

Properties:

  • text (string, required)
    • Rendered label content.
    • Supports substitutions (for example {gi.score}, {ps.p1.score}).
  • font_path (string, optional)
    • If set, this text element uses that font.
    • Use this when one label should stand out or needs a different typeface from the rest of the layout.
  • color (string, default #FFFFFF)
    • #RRGGBB or #RRGGBBAA.
  • size (u32, default 24)
    • Text pixel size.
  • align (string, default left)
    • left, center, right.

Example:

[display]
font_path = "{gv.assets_fonts}/Roboto-Regular.ttf"

[display.layouts.score_panel]
font_path = "{gv.assets_fonts}/Orbitron-SemiBold.ttf"

[[display.layouts.score_panel.elements]]
type = "text"
text = "Score: {gi.score}"
x = 20
y = 620
width = 260
height = 70
size = 42
align = "center"

[[display.layouts.score_panel.elements]]
type = "text"
text = "Bonus"
font_path = "{gv.assets_fonts}/BebasNeue-Regular.ttf"
x = 20
y = 690
width = 260
height = 50
size = 28
align = "center"

type = "leaderboard"

Draws leaderboard entries from play stats.

Font fallback order is the same as type = "text":

  1. leaderboard element font_path
  2. layout font_path
  3. display.font_path
  4. built-in default font search

Properties:

  • play_stat (string, required)
    • Tracked stat key from play_stats.players.<player>.tracked.<stat_key>.
  • order (string, default desc)
    • desc or asc.
  • header (string, optional)
    • Header label text.
    • If blank, runtime uses play_stat.
  • font_path (string, optional)
    • If set, this leaderboard uses that font for both the header and rows.
    • Useful when a leaderboard should match a themed panel or have a more compact font than the rest of the layout.
  • include_players (array of strings, optional)
    • Filters current-session overlay rows only.
  • background_color (string, default #FFFFFF)
    • #RRGGBB or #RRGGBBAA.
    • Use alpha 00 for transparent panel background (for example #00000000).
  • historical_color (string, default #000000)
    • Text color for historical rows.
  • current_color (string, default #1A1DAF)
    • Text color for current-session rows.
  • header_color (string, default #000000)
    • Text color for header.
  • font_size (string, default normal)
    • Presets: small, normal, large.
    • Also supports percent scaling string (for example "125%") relative to normal.
    • Percent values are normalized/clamped to 50%..300%.

Positioning guide

Positioning controls where an element appears on screen.

Coordinate basics

  • x = 0, y = 0 is the top-left corner of the screen.
  • Increasing x moves right.
  • Increasing y moves down.

Position formats

You can set position with:

  • Pixel values (example: x = 10)
  • Percent values (example: x = "70%")
  • Anchor tables (example: x = { anchor = "right", offset = -10 })

Anchors and offsets

Anchors are relative to screen edges/center.

Common anchors:

  • left, right, top, bottom, center

Valid anchors by axis:

  • For x: left, right, center
  • For y: top, bottom, center

Using axis-matching anchors keeps layout intent clear and easier to maintain.

Offset direction rule:

  • Positive x offset moves right; negative moves left
  • Positive y offset moves down; negative moves up

Examples:

x = { anchor = "right", offset = -20 }   # 20 px left from right edge
y = { anchor = "bottom", offset = -20 }  # 20 px up from bottom edge

Use anchor + offset when you want layouts that adapt cleanly to different display sizes.

Sizing guide

Sizing controls how large each element is.

Size formats

width and height can be:

  • Pixels (example: width = 320)
  • Percent (example: width = "75%")

Game element ratio guidance

For Intellivision content, using standard game ratios is usually best:

  • aspect = "force_4x3" is the most common choice
  • aspect = "force_16x9" is useful only when your layout is intentionally widescreen

This helps keep gameplay shape consistent across layouts.

Quick 4:3 sizing reference (1280×720)

When the game is 4:3 inside a 1280x720 display area, the game region is typically:

  • 960x720

That leaves:

  • 160 px side margins each (if centered), or
  • 320 px on one side (if game is anchored fully left or right)

This is a practical reference when designing side panels and overlay assets.

Image sizing and scaling tip

For image overlays/panels, a good practice is:

  • Design assets at the size you intend to display
  • Use scaling = "none" when possible

This avoids unnecessary stretch/fit behavior and gives more predictable visual results.

Valid scaling options (image elements)

type = "image" supports:

  • none (best for pre-sized assets)
    • Uses the image’s native size.
    • Ignores configured width/height for scaling.
  • fit (default)
    • Fits image inside the target box.
    • Preserves aspect ratio (no distortion).
    • Can leave empty space (letterbox/pillarbox).
  • fill
    • Fills the full target box.
    • Preserves aspect ratio.
    • Can crop part of the image.
  • stretch
    • Fills the full target box.
    • Does not preserve aspect ratio.
    • Can visually distort image proportions.

Secondary layout pattern (overlay without game)

A useful layout pattern is a secondary overlay layout that does not include a game element.

Example:

[[display.layouts.secondary_layout_default.elements]]
type = "image"
source = "{game_folder}/{game_file}_big_overlay.png"
fallback = "none"
x = 5
y = { anchor = "top", offset = 20 }
width = "100%"
height = "100%"
scaling = "none"
alpha = 1.0

How it is used:

  • Base layout stays active for normal play
  • Hotkeys or menu actions show/hide/toggle secondary layout as needed

Layout overrides

Layout overrides are usually done at game level.

Common approaches:

  • Define a new game-specific layout name and switch to it
  • Redefine a layout’s elements list when a game needs a different composition

For list-style layout sections, game-level definitions typically replace the list section you define.

Practical tips

  • Start with one simple layout first (game only).
  • Add one overlay element at a time.
  • Keep layout names short and descriptive.
  • Use game-specific layout names when experimenting.
  • Prefer anchor-based placement for reusable layouts.
  • For game view, default to aspect = "force_4x3" unless you have a specific reason not to.
  • For overlays, create assets at target size and prefer scaling = "none".

Next step

Continue to:

Clone this wiki locally