diff --git a/README.md b/README.md
index 13432c47..a6404a2f 100644
--- a/README.md
+++ b/README.md
@@ -169,7 +169,7 @@ curl -X POST http://your-pi-ip:5000/api/v3/plugins/install \
| Plugin | Description | Preview |
|--------|-------------|---------|
-| [Scrolling Text](./plugins/text-display/) | Custom scrolling/static text with configurable fonts and colors | |
+| [Scrolling Text](./plugins/text-display/) | Custom scrolling/static text with configurable fonts and colors |
|
### System (1)
diff --git a/docs/assets/text-display/colors.png b/docs/assets/text-display/colors.png
new file mode 100644
index 00000000..99300270
Binary files /dev/null and b/docs/assets/text-display/colors.png differ
diff --git a/docs/assets/text-display/font-mode.png b/docs/assets/text-display/font-mode.png
new file mode 100644
index 00000000..8bc470c7
Binary files /dev/null and b/docs/assets/text-display/font-mode.png differ
diff --git a/docs/assets/text-display/font-size.png b/docs/assets/text-display/font-size.png
new file mode 100644
index 00000000..6f430b02
Binary files /dev/null and b/docs/assets/text-display/font-size.png differ
diff --git a/docs/assets/text-display/hero.png b/docs/assets/text-display/hero.png
new file mode 100644
index 00000000..c397b188
Binary files /dev/null and b/docs/assets/text-display/hero.png differ
diff --git a/docs/assets/text-display/panel-sizes.png b/docs/assets/text-display/panel-sizes.png
new file mode 100644
index 00000000..38de0ea3
Binary files /dev/null and b/docs/assets/text-display/panel-sizes.png differ
diff --git a/docs/assets/text-display/shots.json b/docs/assets/text-display/shots.json
new file mode 100644
index 00000000..fb07ce68
--- /dev/null
+++ b/docs/assets/text-display/shots.json
@@ -0,0 +1,304 @@
+{
+ "plugin": "text-display",
+ "defaults": {
+ "width": 128,
+ "height": 32,
+ "scale": 6,
+ "freeze_time": "2026-09-02T16:00:00+00:00",
+ "config": {
+ "enabled": true
+ }
+ },
+ "shots": [
+ {
+ "name": "hero",
+ "config": {
+ "scroll": false,
+ "text": "Subscribe to ChuckBuilds",
+ "font_mode": "auto"
+ }
+ },
+ {
+ "name": "mode-manual-long",
+ "config": {
+ "scroll": false,
+ "text": "Subscribe to ChuckBuilds",
+ "font_mode": "manual"
+ },
+ "standalone": false
+ },
+ {
+ "name": "mode-auto-long",
+ "config": {
+ "scroll": false,
+ "text": "Subscribe to ChuckBuilds",
+ "font_mode": "auto"
+ },
+ "standalone": false
+ },
+ {
+ "name": "mode-manual-short",
+ "config": {
+ "scroll": false,
+ "text": "HELLO",
+ "font_mode": "manual"
+ },
+ "standalone": false
+ },
+ {
+ "name": "mode-auto-short",
+ "config": {
+ "scroll": false,
+ "text": "HELLO",
+ "font_mode": "auto"
+ },
+ "standalone": false
+ },
+ {
+ "name": "size-6",
+ "config": {
+ "scroll": false,
+ "text": "Subscribe to ChuckBuilds",
+ "font_size": 6
+ },
+ "standalone": false
+ },
+ {
+ "name": "size-8",
+ "config": {
+ "scroll": false,
+ "text": "Subscribe to ChuckBuilds",
+ "font_size": 8
+ },
+ "standalone": false
+ },
+ {
+ "name": "size-12",
+ "config": {
+ "scroll": false,
+ "text": "Subscribe to ChuckBuilds",
+ "font_size": 12
+ },
+ "standalone": false
+ },
+ {
+ "name": "size-16",
+ "config": {
+ "scroll": false,
+ "text": "Subscribe to ChuckBuilds",
+ "font_size": 16
+ },
+ "standalone": false
+ },
+ {
+ "name": "col-white",
+ "config": {
+ "scroll": false,
+ "text": "READY",
+ "font_mode": "auto"
+ },
+ "standalone": false
+ },
+ {
+ "name": "col-amber",
+ "config": {
+ "scroll": false,
+ "text": "READY",
+ "font_mode": "auto",
+ "text_color": [
+ 255,
+ 176,
+ 0
+ ]
+ },
+ "standalone": false
+ },
+ {
+ "name": "col-green",
+ "config": {
+ "scroll": false,
+ "text": "READY",
+ "font_mode": "auto",
+ "text_color": [
+ 0,
+ 255,
+ 120
+ ]
+ },
+ "standalone": false
+ },
+ {
+ "name": "col-invert",
+ "config": {
+ "scroll": false,
+ "text": "READY",
+ "font_mode": "auto",
+ "text_color": [
+ 0,
+ 0,
+ 0
+ ],
+ "background_color": [
+ 255,
+ 60,
+ 60
+ ]
+ },
+ "standalone": false
+ },
+ {
+ "name": "size-64x32",
+ "config": {
+ "scroll": false,
+ "text": "Subscribe to ChuckBuilds",
+ "font_mode": "auto"
+ },
+ "width": 64,
+ "height": 32,
+ "scale": 8,
+ "standalone": false
+ },
+ {
+ "name": "size-128x32",
+ "config": {
+ "scroll": false,
+ "text": "Subscribe to ChuckBuilds",
+ "font_mode": "auto"
+ },
+ "width": 128,
+ "height": 32,
+ "scale": 8,
+ "standalone": false
+ },
+ {
+ "name": "size-128x64",
+ "config": {
+ "scroll": false,
+ "text": "Subscribe to ChuckBuilds",
+ "font_mode": "auto"
+ },
+ "width": 128,
+ "height": 64,
+ "scale": 8,
+ "standalone": false
+ },
+ {
+ "name": "size-256x32",
+ "config": {
+ "scroll": false,
+ "text": "Subscribe to ChuckBuilds",
+ "font_mode": "auto"
+ },
+ "width": 256,
+ "height": 32,
+ "scale": 4,
+ "standalone": false
+ }
+ ],
+ "composites": [
+ {
+ "name": "font-mode",
+ "columns": 2,
+ "cells": [
+ {
+ "shot": "mode-manual-long",
+ "label": "manual, long text",
+ "sublabel": "font_size 8; the text runs off both edges"
+ },
+ {
+ "shot": "mode-auto-long",
+ "label": "auto, long text",
+ "sublabel": "shrunk to fit the panel exactly"
+ },
+ {
+ "shot": "mode-manual-short",
+ "label": "manual, short text",
+ "sublabel": "stays at font_size 8"
+ },
+ {
+ "shot": "mode-auto-short",
+ "label": "auto, short text",
+ "sublabel": "grown to fill the panel"
+ }
+ ]
+ },
+ {
+ "name": "font-size",
+ "columns": 2,
+ "cells": [
+ {
+ "shot": "size-6",
+ "label": "font_size: 6",
+ "sublabel": "more characters fit before overflow"
+ },
+ {
+ "shot": "size-8",
+ "label": "font_size: 8",
+ "sublabel": "the default"
+ },
+ {
+ "shot": "size-12",
+ "label": "font_size: 12",
+ "sublabel": ""
+ },
+ {
+ "shot": "size-16",
+ "label": "font_size: 16",
+ "sublabel": "only a few characters remain on-panel"
+ }
+ ]
+ },
+ {
+ "name": "colors",
+ "columns": 2,
+ "cells": [
+ {
+ "shot": "col-white",
+ "label": "[255, 255, 255]",
+ "sublabel": "the default"
+ },
+ {
+ "shot": "col-amber",
+ "label": "[255, 176, 0]",
+ "sublabel": "amber"
+ },
+ {
+ "shot": "col-green",
+ "label": "[0, 255, 120]",
+ "sublabel": "green"
+ },
+ {
+ "shot": "col-invert",
+ "label": "black on red",
+ "sublabel": "text_color [0,0,0], background_color [255,60,60]"
+ }
+ ]
+ },
+ {
+ "name": "panel-sizes",
+ "columns": 1,
+ "cells": [
+ {
+ "shot": "size-64x32",
+ "label": "64 x 32",
+ "sublabel": "auto mode shrinks hard to fit"
+ },
+ {
+ "shot": "size-128x32",
+ "label": "128 x 32",
+ "sublabel": "the common two-panel chain"
+ },
+ {
+ "shot": "size-128x64",
+ "label": "128 x 64",
+ "sublabel": "extra height lets auto pick a taller face"
+ },
+ {
+ "shot": "size-256x32",
+ "label": "256 x 32",
+ "sublabel": "a long chain; auto uses the width"
+ }
+ ]
+ }
+ ]
+}
diff --git a/plugins.json b/plugins.json
index 610c1736..8f99c9c0 100644
--- a/plugins.json
+++ b/plugins.json
@@ -853,10 +853,10 @@
"plugin_path": "plugins/text-display",
"stars": 0,
"downloads": 0,
- "last_updated": "2026-07-31",
+ "last_updated": "2026-09-02",
"verified": true,
"screenshot": "",
- "latest_version": "1.1.5"
+ "latest_version": "1.1.6"
},
{
"id": "tide-display",
diff --git a/plugins/text-display/README.md b/plugins/text-display/README.md
index 378ad4d3..2d164641 100644
--- a/plugins/text-display/README.md
+++ b/plugins/text-display/README.md
@@ -1,222 +1,365 @@
------------------------------------------------------------------------------------
-### Connect with ChuckBuilds
+# Scrolling Text
-- Show support on Youtube: https://www.youtube.com/@ChuckBuilds
-- Stay in touch on Instagram: https://www.instagram.com/ChuckBuilds/
-- Want to chat or need support? Reach out on the ChuckBuilds Discord: https://discord.com/invite/uW36dVAtcT
-- Feeling Generous? Support the project:
- - Github Sponsorship: https://github.com/sponsors/ChuckBuilds
- - Buy Me a Coffee: https://buymeacoffee.com/chuckbuilds
- - Ko-fi: https://ko-fi.com/chuckbuilds/
+Put any message on your LED matrix — a greeting, an announcement, a ticker.
+Either scroll it continuously or sit it statically on the panel, in any font,
+size and colour.
------------------------------------------------------------------------------------
+
-# Text Display Plugin
+*Every image in this README is real plugin output, rendered at the true panel
+size and then scaled up so the pixels stay pixels. Nothing here is a mockup.*
-Display custom scrolling or static text messages on your LED matrix with configurable fonts, colors, and animations.
+---
-## Features
+## Table of Contents
-- **Scrolling Text**: Smooth horizontal scrolling animation
-- **Static Text**: Centered text display
-- **Custom Fonts**: Support for TTF and BDF fonts
-- **Custom Colors**: Configurable text and background colors
-- **Adjustable Speed**: Control scroll speed
-- **Auto-sizing**: Automatic text width calculation
-- **Gap Control**: Configurable gap between scroll loops
+1. [What's On Screen](#whats-on-screen)
+2. [Installation](#installation)
+3. [Quick Start](#quick-start)
+4. [Text and Sizing](#text-and-sizing)
+ - [font_mode](#font_mode)
+ - [font_path and font_size](#font_path-and-font_size)
+5. [Scrolling](#scrolling)
+ - [How fast it moves](#how-fast-it-moves)
+ - [Looping and the gap](#looping-and-the-gap)
+6. [Colours](#colours)
+7. [Timing](#timing)
+8. [Recipes](#recipes)
+9. [Panel Sizes](#panel-sizes)
+10. [Troubleshooting](#troubleshooting)
+11. [Development](#development)
+12. [Support](#support)
-## Configuration
+---
-### Example Configuration
+## What's On Screen
-```json
-{
- "enabled": true,
- "text": "Subscribe to ChuckBuilds!",
- "font_mode": "manual",
- "font_path": "assets/fonts/PressStart2P-Regular.ttf",
- "font_size": 8,
- "scroll": true,
- "scroll_speed": 1,
- "scroll_delay": 0.01,
- "scroll_loop": true,
- "scroll_gap_width": 32,
- "target_fps": 120,
- "text_color": [255, 0, 0],
- "background_color": [0, 0, 0],
- "display_duration": 10
-}
-```
+One line of text, vertically centred, drawn either statically or scrolling
+right-to-left.
-### Configuration Options
-
-The full schema lives in
-[`config_schema.json`](config_schema.json) — the web UI form is generated
-from it. Key options:
-
-| Key | Default | Notes |
-|---|---|---|
-| `enabled` | `false` | Master switch |
-| `text` | `"Subscribe to ChuckBuilds"` | The message to display |
-| `font_mode` | `manual` | `manual` uses `font_path`/`font_size` as-is. `auto` picks the largest crisp font that fits the display: static text is sized to fit the whole panel, scrolling text is sized to the panel height — so the same config is readable on a 64x32 and fills a 128x64. |
-| `font_path` | `assets/fonts/PressStart2P-Regular.ttf` | Path to TTF or BDF font file (manual mode) |
-| `font_size` | `8` | Font size in pixels (manual mode) |
-| `scroll` | `true` | Enable horizontal scrolling animation |
-| `scroll_speed` | `1` | Speed multiplier (≈ pixels per frame). Higher = faster. |
-| `scroll_delay` | `0.01` | Sleep between scroll steps in seconds. Lower = smoother but more CPU |
-| `scroll_loop` | `true` | Loop the text instead of stopping after one pass |
-| `scroll_gap_width` | `32` | Pixels of space between scroll loops |
-| `target_fps` | `120` | Target frames per second cap for scroll rendering |
-| `text_color` | `[255, 255, 255]` | RGB text color |
-| `background_color` | `[0, 0, 0]` | RGB background color |
-| `display_duration` | `10` | Seconds the plugin holds the screen |
-| `update_interval` | `60` | Seconds between plugin update ticks |
-
-## Usage
-
-### Basic Static Text
-
-For text that fits on screen:
-```json
-{
- "text": "HELLO",
- "scroll": false,
- "font_size": 12
-}
-```
+Static text is centred horizontally. If it is wider than the panel it simply
+overflows both edges — the plugin does not shrink it for you unless you ask it
+to with [`font_mode: auto`](#font_mode), and does not wrap onto a second line.
+
+Scrolling text starts fully off the right edge and travels left, so at the
+instant a scroll begins the panel is legitimately empty.
+
+---
+
+## Installation
-### Scrolling Message
+**From the Plugin Store (recommended).** Open the LEDMatrix web interface at
+`http://:5000`, go to **Plugin Manager**, find **Scrolling Text** in
+the **Plugin Store** section, and click **Install**.
+
+**Manually.** Copy this directory into your LEDMatrix `plugin-repos/` and
+restart the display service.
+
+`enabled` defaults to **`false`**, so nothing appears until you switch the
+plugin on.
+
+---
+
+## Quick Start
-For longer messages:
```json
{
- "text": "This is a long message that will scroll across the display",
- "scroll": true,
- "scroll_speed": 40
+ "text-display": {
+ "enabled": true,
+ "text": "Welcome to the workshop"
+ }
}
```
-### Custom Styling
+A fully specified configuration:
```json
{
- "text": "ALERT!",
- "text_color": [255, 0, 0],
- "background_color": [0, 0, 0],
- "font_path": "assets/fonts/PressStart2P-Regular.ttf",
- "font_size": 10
+ "text-display": {
+ "enabled": true,
+ "text": "Subscribe to ChuckBuilds",
+ "font_mode": "auto",
+ "font_path": "assets/fonts/PressStart2P-Regular.ttf",
+ "font_size": 8,
+ "scroll": true,
+ "scroll_loop": true,
+ "scroll_speed": 1,
+ "scroll_delay": 0.01,
+ "target_fps": 120,
+ "scroll_gap_width": 32,
+ "text_color": [255, 255, 255],
+ "background_color": [0, 0, 0],
+ "display_duration": 10,
+ "update_interval": 60
+ }
}
```
-## Font Support
+---
-### TTF Fonts (TrueType)
+## Text and Sizing
-Most common, widely available:
-```json
-{
- "font_path": "assets/fonts/PressStart2P-Regular.ttf",
- "font_size": 8
-}
-```
+| Option | Type | Default | What it does |
+|--------|------|---------|--------------|
+| `text` | string | `"Subscribe to ChuckBuilds"` | The message to display |
+| `font_mode` | string | `manual` | `manual` or `auto` — see below |
+| `font_path` | string | `assets/fonts/PressStart2P-Regular.ttf` | Font file, relative to the project root or absolute |
+| `font_size` | number | `8` | Size in pixels. **Manual mode only** |
-### BDF Fonts (Bitmap)
+### `font_mode`
-Optimized for LED matrices:
-```json
-{
- "font_path": "assets/fonts/4x6.bdf",
- "font_size": 6
-}
+This is the setting that decides whether you have to think about sizing at all.
+
+
+
+- **`manual`** (the default) uses `font_size` exactly as configured. Long text
+ overflows the panel; short text stays small.
+- **`auto`** ignores `font_size` and picks the largest crisp size that fits the
+ panel. Long text is shrunk to fit; short text is grown to fill.
+
+`auto` is the right choice for a static message you want readable across a
+room, and for any text whose length you do not control. Stay on `manual` when
+you want a consistent size regardless of what the message says — a ticker that
+changes text should not change size with it.
+
+### `font_path` and `font_size`
+
+`font_path` accepts both TrueType (`.ttf`) and bitmap (`.bdf`) fonts:
+
+- **TTF** scales to any `font_size`. `PressStart2P-Regular.ttf` (the default) is
+ a chunky 8-bit face that stays legible at a distance; `4x6-font.ttf` fits far
+ more characters per line.
+- **BDF** is a bitmap face drawn at one fixed pixel size. It renders crisply,
+ but `font_size` cannot change it — the file's own size wins.
+
+
+
+Larger sizes are more readable but fit less on the panel, which is what makes
+`scroll` or `font_mode: auto` necessary for anything longer than a word or two.
+
+---
+
+## Scrolling
+
+| Option | Type | Default | What it does |
+|--------|------|---------|--------------|
+| `scroll` | boolean | `true` | Scroll the text, or draw it statically |
+| `scroll_loop` | boolean | `true` | Loop continuously, or scroll once and stop |
+| `scroll_speed` | number | `1` | Pixels moved per frame |
+| `scroll_delay` | number | `0.01` | Seconds per frame |
+| `target_fps` | number | `120` | Target frame rate hint |
+| `scroll_gap_width` | number | `32` | Blank pixels between the end and the restart |
+
+### How fast it moves
+
+Three settings look like they control speed. Only two of them actually set it:
+
+```text
+pixels per second = scroll_speed / scroll_delay
```
-## Tips & Best Practices
+So the defaults — 1 pixel per frame every 0.01s — give 100 px/s.
+
+- **`scroll_speed`** is pixels per *frame*, not per second. It is clamped to a
+ maximum of 5; above that the movement reads as jumping rather than scrolling,
+ and the plugin logs a warning if you set more. Values above 5 in an old config
+ usually mean it was written when this was pixels-per-second.
+- **`scroll_delay`** is the throttle — seconds between frames. Lowering it
+ raises both the frame rate and the CPU cost.
+- **`target_fps`** is a pacing hint passed to the core's scroll helper, clamped
+ to 30–200. It does not by itself change the pixels-per-second figure above;
+ raising it without lowering `scroll_delay` will not make text move faster.
+
+To make text move faster, prefer raising `scroll_speed` a little (1 → 2) over
+driving `scroll_delay` very low. To make it smoother, lower `scroll_delay`.
+
+### Looping and the gap
+
+With `scroll_loop: true` the message repeats forever, and `scroll_gap_width`
+sets how much blank panel passes between the last character and the first
+coming round again. A gap roughly equal to your panel width gives the cleanest
+loop — the message is fully gone before it returns. The default of 32 suits a
+64-wide panel; on a 128-wide chain, try 128.
+
+With `scroll_loop: false` the text scrolls past once and stops. Pair it with
+`display_duration` long enough for a full pass, or the plugin's turn will end
+mid-message.
+
+> **A still cannot show motion.** There is no screenshot of scrolling in this
+> README because a single frame captured at the start of a scroll is an empty
+> panel — the text has not entered yet. That is correct behaviour, not a fault.
-### For Scrolling Text
+---
-1. **Adjust speed for readability**: `scroll_speed` is a multiplier, not px/s.
- Values around `1`–`2` are typical; higher values scroll faster.
-2. **Tune smoothness with `scroll_delay`**: lower (0.005) = smoother but
- more CPU; higher (0.05) = choppier but lighter.
-3. **Set appropriate gap**: a `scroll_gap_width` equal to your display width
- produces clean loops.
-4. **Test message length**: very long messages benefit from a higher
- `target_fps` cap and lower `scroll_delay`.
+## Colours
-### For Static Text
+| Option | Type | Default | What it does |
+|--------|------|---------|--------------|
+| `text_color` | array | `[255, 255, 255]` | Text colour, `[R, G, B]` |
+| `background_color` | array | `[0, 0, 0]` | Panel background, `[R, G, B]` |
-1. **Center short messages**: Disable scroll for text that fits
-2. **Choose appropriate font size**: Match display height
-3. **Use contrasting colors**: Ensure good visibility
+
-### Font Selection
+A non-black `background_color` lights every pixel on the panel, which draws
+noticeably more power and is much brighter in a dark room. Use it for a
+deliberate alert, not as a default.
-1. **For LED matrices**: Pixel fonts (BDF) work best
-2. **For clarity**: Use fonts designed for small sizes
-3. **For style**: TrueType fonts offer more options
+---
-## Common Use Cases
+## Timing
+
+| Option | Type | Default | What it does |
+|--------|------|---------|--------------|
+| `display_duration` | number | `10` | Seconds the plugin holds the panel per turn |
+| `update_interval` | integer | `60` | Seconds between refreshes of the text |
+
+For scrolling text, `display_duration` should be long enough for at least one
+full pass, or viewers only ever see the middle of the message. A rough guide:
+
+```text
+seconds for one pass ≈ (text width + panel width + scroll_gap_width) / (scroll_speed / scroll_delay)
+```
+
+`update_interval` matters little here because the text is static configuration
+rather than fetched data — it only decides how quickly a config change is
+picked up.
+
+---
+
+## Recipes
+
+**A static announcement.** Large, centred, no motion:
-### Announcements
```json
-{
- "text": "WELCOME!",
- "scroll": false,
- "font_size": 12,
- "text_color": [0, 255, 0]
-}
+{ "text": "WELCOME!", "scroll": false, "font_mode": "auto",
+ "text_color": [0, 255, 0] }
```
-### Ticker Messages
+**A news-style ticker.** Long message, continuous loop, slightly quicker:
+
```json
-{
- "text": "Breaking News: LED matrices are awesome! Stay tuned for more...",
- "scroll": true,
- "scroll_speed": 1.5
-}
+{ "text": "Breaking: LED matrices are awesome. Stay tuned for more...",
+ "scroll": true, "scroll_speed": 1.5, "scroll_gap_width": 128,
+ "display_duration": 30 }
```
-### Call to Action
+**A call to action.** Coloured, looping, sized to the panel:
+
```json
-{
- "text": "Subscribe to ChuckBuilds on YouTube!",
- "scroll": true,
- "scroll_speed": 2,
- "text_color": [255, 0, 0]
-}
+{ "text": "Subscribe to ChuckBuilds on YouTube!", "scroll": true,
+ "scroll_speed": 2, "text_color": [255, 0, 0] }
```
+**A one-shot message.** Scrolls past once and stops, then hands the panel back:
+
+```json
+{ "text": "Build complete", "scroll": true, "scroll_loop": false,
+ "display_duration": 20 }
+```
+
+### Choosing a font for a panel
+
+Bitmap (`.bdf`) faces are drawn pixel-exact and stay crisp, which suits an LED
+matrix better than a scaled outline font. TrueType gives more choice and any
+size you like, at the cost of soft edges at awkward sizes. Whichever you pick,
+prefer a face designed for small sizes — a display font intended for print
+turns to mush below about 10 pixels.
+
+---
+
+## Panel Sizes
+
+
+
+In `auto` mode the plugin uses whatever the panel gives it: a 64-wide panel
+forces a small face, while a 256-wide chain lets the same message render large.
+In `manual` mode the size is fixed, so a wider panel simply shows more of the
+message before it overflows.
+
+---
+
## Troubleshooting
-**Text not visible:**
-- Check text_color is different from background_color
-- Verify text string is not empty
-- Check font_path points to valid font file
+**Nothing appears.**
+`enabled` defaults to `false`. Check it is `true`.
+
+**The panel is blank but I set text.**
+If `scroll` is on, the message begins off the right edge — a blank panel at the
+start of a pass is expected. If it stays blank, check `text_color` is not the
+same as `background_color`.
+
+**The text is cut off at both edges.**
+That is static `manual` mode with text wider than the panel. Switch to
+`font_mode: auto`, lower `font_size`, or turn on `scroll`.
+
+**Scrolling looks jumpy.**
+`scroll_speed` is pixels per *frame*. Values above 2 visibly step; above 5 the
+plugin clamps and logs a warning. Lower `scroll_speed` and lower `scroll_delay`
+instead.
+
+**I raised `target_fps` and nothing got faster.**
+It is a pacing hint, not the speed control. Speed is
+`scroll_speed / scroll_delay` — see [How fast it moves](#how-fast-it-moves).
+
+**I only ever see the middle of the message.**
+`display_duration` is ending the turn before a full pass completes. Raise it,
+or shorten the text.
-**Scrolling too fast/slow:**
-- Adjust `scroll_speed` (multiplier, default `1`). Try values between `0.5` and `3`.
-- For finer control, also tune `scroll_delay` and `target_fps`.
+**`font_size` has no effect.**
+Either `font_mode` is `auto` (which chooses the size itself) or `font_path`
+points at a `.bdf`, which is drawn at its own fixed pixel size.
-**Font not loading:**
-- Verify font_path is correct
-- Check font file exists
-- Ensure font file permissions are correct
-- For BDF fonts, ensure freetype-py is installed
+**The font did not change.**
+`font_path` is resolved relative to the LEDMatrix project root, not to the
+plugin directory. Check the log for a font-loading warning.
-**Text appears cut off:**
-- Reduce font_size
-- For static text, ensure text fits display width
-- For scrolling, text should extend beyond display
+---
+
+## Development
+
+### Project structure
+
+```text
+text-display/
+├── manifest.json # Plugin metadata and version history
+├── manager.py # TextDisplayPlugin
+├── config_schema.json # Settings schema; source of truth for defaults
+└── README.md
+```
+
+Scrolling is delegated to the core's `ScrollHelper` in frame-based mode, which
+is the same mechanism the stock and leaderboard tickers use — so scrolling here
+behaves consistently with those.
+
+### Performance
+
+Scrolling redraws the panel every frame, so it costs meaningfully more CPU than
+static text. On a Raspberry Pi driving a large chain, prefer the default
+`scroll_delay` of `0.01` over lower values, and remember that a non-black
+`background_color` lights every pixel.
+
+### Regenerating the images in this README
+
+```bash
+python scripts/render_docs_assets.py --plugin text-display
+```
-## Performance Notes
+`--check` verifies the committed images still match what the plugin renders.
-- Scrolling text uses a pre-rendered cache for smooth animation
-- The render loop targets `target_fps` (default 120) and sleeps
- `scroll_delay` between steps
-- Text cache is created once at first render and reused
-- Font loading happens once at initialization
+---
-## License
+## Support
-GPL-3.0 License - see main LEDMatrix repository for details.
+- YouTube:
+- Instagram:
+- Discord:
+- Sponsor: [GitHub Sponsors](https://github.com/sponsors/ChuckBuilds) ·
+ [Buy Me a Coffee](https://buymeacoffee.com/chuckbuilds) ·
+ [Ko-fi](https://ko-fi.com/chuckbuilds/)
+Released under the GNU General Public License v3.0 — see [LICENSE](LICENSE).
diff --git a/plugins/text-display/manifest.json b/plugins/text-display/manifest.json
index fe15f87d..b1b52cb3 100644
--- a/plugins/text-display/manifest.json
+++ b/plugins/text-display/manifest.json
@@ -1,7 +1,7 @@
{
"id": "text-display",
"name": "Text Display",
- "version": "1.1.5",
+ "version": "1.1.6",
"author": "ChuckBuilds",
"description": "Display custom scrolling or static text with configurable fonts, colors, and scroll speed. Perfect for announcements, messages, or custom displays.",
"category": "display",
@@ -24,6 +24,11 @@
}
},
"versions": [
+ {
+ "version": "1.1.6",
+ "released": "2026-09-02",
+ "ledmatrix_min": "2.0.0"
+ },
{
"version": "1.1.5",
"released": "2026-07-31",
@@ -74,7 +79,7 @@
"ledmatrix_min_version": "2.0.0"
}
],
- "last_updated": "2026-07-31",
+ "last_updated": "2026-09-02",
"stars": 0,
"downloads": 0,
"verified": true,