-
Notifications
You must be signed in to change notification settings - Fork 0
03 05 Config Display Layouts
This page covers layout definitions used by [display].
If you have not read the display overview yet, start with:
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)-
#RRGGBBor#RRGGBBAA. - If unset, uses global
display.background_color.
-
-
font_path(string, optional)- Default font for
type = "text"andtype = "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.
- Default font for
-
enable_game_border_background_color(bool, defaultfalse)- When
true, layout background follows game border color when available. - Falls back to layout/global background color if game color is unavailable.
- When
[display]
default_layout = "fullscreen"
[display.layouts.fullscreen]
[[display.layouts.fullscreen.elements]]
type = "game"
x = 0
y = 0
width = "100%"
height = "100%"
aspect = "force_4x3"Elements render in the order they are listed.
Common pattern:
- Draw
type = "game"first - Draw overlays/images/text after that
This makes overlays appear on top of gameplay.
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.0Because draw order is top-to-bottom, this renders as:
- game first
- panel image on top
- text on top of both
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.
Draws the game view.
Properties:
-
aspect(string, defaultmaintain)- 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.
- Only valid on
How aspect works:
- The game element gives Sprint Boost a box to draw into.
-
aspectdecides 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:3shape 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 a4:3game region inside it. - Use this when you want a predictable classic-TV style game presentation.
- Forces the visible game result to fit a
-
force_16x9- Forces the visible game result to fit a
16:9shape 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.
- Forces the visible game result to fit a
-
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_4x3for most standard gameplay layouts. - Use
integer_scalewhen you want the crispest, most pixel-clean result. - Use
maintainwhen you want to preserve the natural game shape without forcing a specific presentation ratio. - Use
stretchonly when distortion is acceptable. - Use
force_16x9only 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_16x9orstretch, depending on whether distortion is acceptable.
Border table fields:
-
enabled(bool, defaultfalse) -
mode(string, defaultfit; currently supported:fit) -
base_size(pixels or percent string, default0) -
left_adjust,right_adjust,top_adjust,bottom_adjust(i32, default0) -
color(string, defaultdisplay_background)-
game,display_background, or hex (#RRGGBB/#RRGGBBAA)
-
-
center_on(string, defaultgame_frame)-
game_frameorbordered_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_sizesets the starting thickness for all four sides. -
left_adjust,right_adjust,top_adjust, andbottom_adjustlet you fine-tune each side after that. - A percent
base_sizeuses 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 = trueis layout-wide. It changes the layout background to follow the game border color. - That is separate from the game element
borderitself. 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 = 0Draws 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
sourcecannot be loaded. - Use
"none"to disable fallback.
- Used if
-
scaling(string, defaultfit)-
none,fit,fill,stretch.
-
-
blend(string, defaultnormal)-
normal,multiply,add,overlay.
-
-
trim_transparent_edges(bool or table, default disabled)-
trueis shorthand for{ alpha_threshold = 0 }. - Removes fully transparent outer rows and columns before drawing.
-
alpha_threshold > 0is allowed, but lossy.
-
-
transparent_region_optimization(bool or table, default disabled)-
trueis 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_heightdefault to192/128. -
alpha_threshold > 0is allowed, but lossy.
-
Supported image formats:
PNG-
JPEG/JPG
Practical recommendation:
- Prefer
PNGfor 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_layerfor 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 }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_elementor image-viewer hotkey commands.
- Needed when you want to target this viewer from
-
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=includeorexclude -
pattern= basename wildcard match using*and?
-
- Filters files found through
-
source_order(string, defaultalphabetical)-
alphabetical,natural,random
-
-
scaling(string, defaultfit)-
none,fit,fill,stretch
-
-
sampling(string, defaultlinear)-
linearornearest
-
-
background_color(string, optional)- Fills the viewer box behind the image.
-
wrap_navigation(bool, defaulttrue)- If
true, moving past the last image wraps to the first, and vice versa.
- If
-
input_when_focused(string, defaultsuppress_handled)-
suppress_handled,suppress_all,passthrough
-
-
zoom(bool or table, default enabled)-
trueis shorthand for enabled. - Table fields:
enabledminmaxstep
-
-
pan(bool or table, default enabled)-
trueis shorthand for enabled. - Table field:
enabled
-
-
focus_indicator(bool or table, default enabled)-
trueis shorthand for enabled. - Table fields:
enabledcolorthickness
-
How source loading works:
-
source_folderandsource_listare additive. - Files from
source_foldercan be filtered bysource_filters. -
source_listis always explicit and is not filtered bysource_filters.
How source_filters works:
- If you use one or more
includefilters, a file must match at least one include filter to be kept. - Any
excludefilter 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
_
- 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
idif 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 }Draws text values (for example game info or tracked stats).
Font fallback order:
display.layouts.<layout>.elements[].font_pathdisplay.layouts.<layout>.font_pathdisplay.font_path- 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)-
#RRGGBBor#RRGGBBAA.
-
-
size(u32, default24)- Text pixel size.
-
align(string, defaultleft)-
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"Draws leaderboard entries from play stats.
Font fallback order is the same as type = "text":
- leaderboard element
font_path - layout
font_path display.font_path- built-in default font search
Properties:
-
play_stat(string, required)- Tracked stat key from
play_stats.players.<player>.tracked.<stat_key>.
- Tracked stat key from
-
order(string, defaultdesc)-
descorasc.
-
-
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)-
#RRGGBBor#RRGGBBAA. - Use alpha
00for 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, defaultnormal)- Presets:
small,normal,large. - Also supports percent scaling string (for example
"125%") relative tonormal. - Percent values are normalized/clamped to
50%..300%.
- Presets:
Positioning controls where an element appears on screen.
-
x = 0,y = 0is the top-left corner of the screen. - Increasing
xmoves right. - Increasing
ymoves down.
You can set position with:
- Pixel values (example:
x = 10) - Percent values (example:
x = "70%") - Anchor tables (example:
x = { anchor = "right", offset = -10 })
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
xoffset moves right; negative moves left - Positive
yoffset 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 edgeUse anchor + offset when you want layouts that adapt cleanly to different display sizes.
Sizing controls how large each element is.
width and height can be:
- Pixels (example:
width = 320) - Percent (example:
width = "75%")
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.
When the game is 4:3 inside a 1280x720 display area, the game region is typically:
960x720
That leaves:
-
160 pxside margins each (if centered), or -
320 pxon one side (if game is anchored fully left or right)
This is a practical reference when designing side panels and overlay assets.
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.
type = "image" supports:
-
none(best for pre-sized assets)- Uses the image’s native size.
- Ignores configured
width/heightfor 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.
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.0How it is used:
- Base layout stays active for normal play
- Hotkeys or menu actions show/hide/toggle secondary layout as needed
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
elementslist when a game needs a different composition
For list-style layout sections, game-level definitions typically replace the list section you define.
- Start with one simple layout first (
gameonly). - 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".
Continue to: