image widget
The image widget displays a single still image at native physical resolution — each LED maps to one pixel in the source. Use it for logos, product photos, brand artwork, or any single-frame graphic. This widget is essentially gif with a single frame: it shares the gif widget’s full feature surface (fit modes, text overlay, two-row, animation, border) minus the per-frame mechanics — the only widget-specific knob is hold_time instead of play_count.

Minimal example
[[playlist.section.widget]]type = "image"path = "config/assets/my_logo.png"hold_time = 4.0Options
Section titled “Options”Common patterns
Section titled “Common patterns”Static logo (no text)
Section titled “Static logo (no text)”
[[playlist.section.widget]]type = "image"path = "config/assets/phoenix_transparent.png"fit = "pillarbox"image_align = "center"hold_time = 4.0Image with scrolling text overlay (scroll_over — text in front of the image)
Section titled “Image with scrolling text overlay (scroll_over — text in front of the image)”
[[playlist.section.widget]]type = "image"path = "config/assets/phoenix_transparent.png"fit = "pillarbox"image_align = "left"text = "Follow us on Instagram!"text_align = "scroll_over"hold_time = 7.0font_color = [200, 100, 255]Scrolling text behind a transparent silhouette (scroll — text walks under the image; requires a PNG with transparency)
Section titled “Scrolling text behind a transparent silhouette (scroll — text walks under the image; requires a PNG with transparency)”
[[playlist.section.widget]]type = "image"path = "config/assets/phoenix_transparent.png"fit = "pillarbox"image_align = "left"text = "Scrolling behind the phoenix!"text_align = "scroll"font_color = [200, 200, 255]Wrap mode (seamless marquee with a separator between repeats)
Section titled “Wrap mode (seamless marquee with a separator between repeats)”
Setting text_wrap = true on a scroll or scroll_over marquee runs the text seamlessly — at every tick at least one full copy of the message is on the panel, with a configurable separator drawn between repeats. Use this for short slogans or repeating banners where the gap of an empty canvas between traversals breaks the rhythm.
[[playlist.section.widget]]type = "image"path = "config/assets/phoenix_transparent.png"text = "hi friend"text_wrap = truetext_align = "scroll_over"font_color = [120, 230, 255] # cyantext_separator_color = [255, 200, 80] # steady yellow separator# default separator: " • " # (omit text_separator to use it)| Field | Type | Default | Meaning |
|---|---|---|---|
text_wrap | bool | false | Toggle seamless wrap. Requires text_align ∈ ("scroll", "scroll_over"). |
text_separator | string | " • " (when text_wrap = true) | Glyph(s) between repeats. "" falls back to a two-space gap. |
text_separator_color | color spec | inherit font_color | Color for the separator; whole-string provider (one hue per frame). |
Notes:
text_loopsin wrap mode means minimum number of cycle traversals (one cycle = text + separator), not the off-right → off-left definition the default marquee uses. On thetwo_rowwidget the equivalent field isbottom_text_loops(prefixed to matchbottom_text_wrapand friends on that widget).- Two-row mode (
bottom_textset) refusestext_wrapin v1. - The separator renders in the widget’s
font; a per-separator font override isn’t part of v1.
Wrap mode (bottom row, seamless marquee)
Section titled “Wrap mode (bottom row, seamless marquee)”
Setting bottom_text_wrap = true in two-row mode (when bottom_text is set) runs the bottom row as a seamless marquee — chasing itself across the canvas with a separator between repeats. Top row stays held at top_align; only the bottom row wraps. Engages even when the bottom text fits — predictable.
[[playlist.section.widget]]type = "image"path = "config/assets/phoenix_transparent.png"top_text = "BREAKING"top_color = [225, 48, 108]bottom_text = "tap to subscribe"bottom_text_wrap = truebottom_text_separator = " * " # default: " • "bottom_text_separator_color = "rainbow" # default: inherit bottom_color| Field | Type | Default | Meaning |
|---|---|---|---|
bottom_text_wrap | bool | false | Toggle seamless wrap on the bottom row. Requires bottom_text non-empty. |
bottom_text_separator | string | " • " (when bottom_text_wrap = true) | Glyph(s) between bottom-row repeats. "" falls back to a two-space gap. |
bottom_text_separator_color | color spec | inherit bottom_color | Color for the bottom separator; whole-string provider (one hue per frame). |
Notes:
bottom_text_wrapalways wraps when set, even if the bottom text fits the canvas.- Top row never wraps (refused by validation; no
top_text_wrapfield exists). - The separator color inherits
bottom_color(NOTfont_color) — separator is part of the bottom row. - v1’s
text_wrapstays single-row only — in two-row mode usebottom_text_wrap. bottom_text_wrapis only allowed inmode = "slideshow". Inticker/one_at_a_timemodes, validation refuses it (those modes expect widgets to terminate naturally).- Loop count for the bottom-row wrap: use
text_loopshere on gif/image widgets (same field that controls single-row wrap loops). On thetwo_rowwidget — which is always two-row, not optionally — the equivalent field is namedbottom_text_loopsto match its bottom-prefix convention. The validator catches the cross-widget typo (rule 29) and points you at the right name.
Scroll-through mode (bottom row, one offscreen pass)
Section titled “Scroll-through mode (bottom row, one offscreen pass)”
Setting bottom_text_scroll = "scroll_through" in two-row mode forces the bottom row to do an offscreen-to-offscreen scroll on every visit — text starts fully off the right edge and ends fully off the left edge — instead of the default “held when it fits, scroll only on overflow” behavior. bottom_align is ignored.
[[playlist.section.widget]]type = "image"path = "config/assets/phoenix_transparent.png"fit = "pillarbox"image_align = "right"top_text = "@firebird.demo"top_color = [225, 48, 108]bottom_text = "Firebird Yoga — now booking spring sessions — your first class is free."bottom_color = [255, 240, 200]bottom_text_scroll = "scroll_through"| Field | Type | Default | Meaning |
|---|---|---|---|
bottom_text_scroll | string | "marquee" | Bottom-row scroll style. "marquee" preserves the held-on-fit / scroll-on-overflow default; "scroll_through" forces offscreen-to-offscreen passes on every visit. |
Notes:
- Mutually exclusive with
bottom_text_wrap = true— pick one. Scroll-through is one pass; wrap is a seamless tile. - Requires non-empty
bottom_text(two-row mode). Single-row text scroll lives undertext_align = "scroll"/"scroll_over". - Only valid in
mode = "slideshow". Inticker/one_at_a_timemodes validation refuses it (rule 32). hold_timeis honored as a floor: at least one full pass per visit, ortext_loops × (canvas_width + bottom_width)if larger, or whateverhold_timeadmits — whichever is greatest. The bottom row keeps cycling until that floor is met.
Two-row text overlay (set bottom_text to switch modes — image sits underneath)
Section titled “Two-row text overlay (set bottom_text to switch modes — image sits underneath)”
[[playlist.section.widget]]type = "image"path = "config/assets/phoenix_transparent.png"fit = "pillarbox"image_align = "right"top_text = "@firebird.demo"top_font = "5x8"top_color = [225, 48, 108]bottom_text = "Open daily 10am – 8pm"bottom_font = "5x8"bottom_color = [255, 240, 200]hold_time = 7.0Typewriter overlay (single-row only — letters type in next to the image)
Section titled “Typewriter overlay (single-row only — letters type in next to the image)”
[[playlist.section.widget]]type = "image"path = "config/assets/phoenix_transparent.png"fit = "pillarbox"image_align = "left"text = "firebird"text_align = "right"font_color = "rainbow"animation = "typewriter"hold_time = 8.0Typewriter + rainbow border (frame the panel as the text appears)
Section titled “Typewriter + rainbow border (frame the panel as the text appears)”
[[playlist.section.widget]]type = "image"path = "config/assets/phoenix_transparent.png"fit = "pillarbox"image_align = "left"text = "firebird"text_align = "right"font_color = [255, 255, 255]animation = "typewriter"border = "rainbow"hold_time = 8.0Typewriter is single-row only
Section titled “Typewriter is single-row only”animation = "typewriter" rejects three combinations: bottom_text set (two-row layout), text_align = "scroll" / "scroll_over" (cursor moves each tick), or empty text. Drop the conflicting setting; typewriter still composes with font_color = "rainbow" and border = "rainbow".
text_align="scroll" needs transparency
Section titled “text_align="scroll" needs transparency”Stretch fills every pixel with image, leaving no transparent regions for the text to scroll through. Use text_align="scroll_over" for a fullscreen marquee, or pair scroll with a transparent PNG and fit="pillarbox".
text_x_offset is rejected in scroll modes
Section titled “text_x_offset is rejected in scroll modes”Static offsets don’t compose with the per-tick scroll cursor. Set text_x_offset = 0 or change text_align to "left" or "right".
hold_time must be at least 0.05
Section titled “hold_time must be at least 0.05”hold_time is in seconds, not milliseconds. Anything under 50 ms is rejected — nothing shorter than one engine tick can actually display.
Animated source files only show frame 0
Section titled “Animated source files only show frame 0”The image widget decodes only the first frame of .gif / .webp files. Use the gif widget for animation — it shares the entire image text-overlay surface plus per-frame mechanics.