mirror of
https://github.com/ChuckBuilds/LEDMatrix.git
synced 2026-10-18 04:49:13 +00:00
Whole-pixel motion was previously only available at multiples of the refresh rate -- 100, 200, 300 px/s on a 100Hz panel. 100 px/s crosses a 256px panel in 2.6s, which is brisk for reading, and everything slower had to blend (blur) or repeat frames unevenly (judder). There was no way to ask for 50 px/s and get clean motion. SwapOnVSync takes a framerate_fraction the display manager never passed. It holds each frame for N panel refreshes; the panel keeps refreshing at its full rate throughout, so holding costs nothing in flicker and only changes how often a NEW image is presented. That turns 50 px/s into one whole pixel every second refresh instead of half a pixel every refresh. The crisp speeds are therefore refresh_hz / hold * pixels_per_frame, and that ladder depends on the panel: a Pi Zero on a long chain has a different set of good speeds from a Pi 4 on a short one. crisp_ladder() enumerates them and solve_crisp() picks the best match for a requested speed. solve_crisp weights motion quality rather than picking the numerically nearest entry, which matters more than it sounds. Asked for 30 px/s, nearest-by-value answers 28.6 -- 2px jumps at 14fps -- over 33.3, which is single-pixel motion at 33fps and obviously better on the panel. The target is also clamped into the ladder's range first, because relative error saturates near 1.0 for a target far outside it and the quality penalty would otherwise answer "10000 px/s" with the slowest entry. configure() snaps to the ladder and applies the hold when given a display manager. Without one the hold silently cannot happen and motion falls back to fractional pixels, so it warns rather than failing quietly. set_frame_hold() resets to 1 when scrolling stops, so one plugin's pacing cannot leak into whatever is on screen next. scripts/scroll_speeds.py is the user-facing part: it prints the ladder for the configured rate, measures what the panel ACTUALLY manages (--measure, for hardware that cannot reach its configured limit), highlights the nearest option to a wanted speed, and demos one live. It never starts or stops the display service itself -- doing that inside a script stranded the panel twice today. Speeds below ~20 px/s remain stepped regardless. That is the pixel pitch, not a software limit. Also fixes the dirty-tracking test spy, which stubbed SwapOnVSync with a single-argument function and would have masked the new call as a failed push, and rewrites a configure() test that had started passing for the wrong reason: it asserted a judder warning, which snapping now prevents, and was matching the unrelated "hold could not be applied" warning instead. 183 tests pass on the Pi. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
LEDMatrix Documentation
This directory contains guides, references, and architectural notes for the LEDMatrix project. If you are setting up a Pi for the first time, start with the project root README — it covers hardware, OS imaging, and the one-shot installer. The pages here go deeper.
I'm a new user
- GETTING_STARTED.md — first-time setup walkthrough
- WEB_INTERFACE_GUIDE.md — using the web UI
- PLUGIN_STORE_GUIDE.md — installing and managing plugins
- WIFI_NETWORK_SETUP.md — WiFi and AP-mode setup
- TROUBLESHOOTING.md — common issues and fixes
- SSH_UNAVAILABLE_AFTER_INSTALL.md — recovering SSH after install
- CONFIG_DEBUGGING.md — diagnosing config problems
- LOW_MEMORY_BOARDS.md — Pi Zero 2 W / 3B+ / 1GB Pi 4 memory limits
I want to write a plugin
Start here:
- PLUGIN_DEVELOPMENT_GUIDE.md — end-to-end workflow
- PLUGIN_QUICK_REFERENCE.md — cheat sheet
- PLUGIN_API_REFERENCE.md — display, cache, and plugin-manager APIs
- PLUGIN_ERROR_HANDLING.md — error-handling patterns
- DEV_PREVIEW.md — preview plugins on your dev machine without a Pi
- EMULATOR_SETUP_GUIDE.md — running the matrix emulator
Going deeper:
- ADVANCED_PLUGIN_DEVELOPMENT.md — advanced patterns
- PLUGIN_ARCHITECTURE_SPEC.md — original plugin-system design spec (historical; see its banner for what has drifted)
- PLUGIN_DEPENDENCY_GUIDE.md / PLUGIN_DEPENDENCY_TROUBLESHOOTING.md
- PLUGIN_WEB_UI_ACTIONS.md (+ example JSON)
- PLUGIN_CUSTOM_ICONS.md
- PLUGIN_REGISTRY_SETUP_GUIDE.md (+ registry template)
- STARLARK_APPS_GUIDE.md — Starlark-based mini-apps
- widget-guide.md — widget development
- ADAPTIVE_LAYOUT.md — render legibly on any panel size (opt-in font/layout scaling)
- plugin-safety-harness.md — test a plugin across every screen and matrix size
Configuring plugins
- PLUGIN_CONFIG_QUICK_START.md — minimal config you need
- PLUGIN_CONFIGURATION_GUIDE.md — schema design
- PLUGIN_CONFIGURATION_TABS.md — multi-tab UI configs
- PLUGIN_CONFIG_ARCHITECTURE.md — how the config system works
- PLUGIN_CONFIG_CORE_PROPERTIES.md — properties every plugin honors
Advanced features
- ADVANCED_FEATURES.md — Vegas scroll, on-demand display, cache management, background services, permissions
- FONT_MANAGER.md — font system
- SKIN_SYSTEM.md — skin architecture for sports scoreboards
- CREATING_SKINS.md — writing and validating a skin
Reference
- CONFIG_REFERENCE.md — every key in config.json and config_secrets.json
- REST_API_REFERENCE.md — all web-interface HTTP endpoints
- PLUGIN_API_REFERENCE.md — Python APIs available to plugins
- DEVELOPER_QUICK_REFERENCE.md — common dev tasks
- PLUGIN_IMPLEMENTATION_SUMMARY.md — what the plugin system actually does
Contributing to LEDMatrix itself
- DEVELOPMENT.md — environment setup
- HOW_TO_RUN_TESTS.md — running the test suite
- MULTI_ROOT_WORKSPACE_SETUP.md — multi-repo workspace
- MIGRATION_GUIDE.md — breaking changes between releases
- SPORTS_UNIFICATION.md — how the sports scoreboard base classes are organized
Archive
docs/archive/ holds older guides that have been superseded or describe
features that have been removed. They are kept for historical context and
git history but should not be relied on.
Contributing to the docs
- Markdown only, professional tone, minimal emoji.
- Prefer adding to an existing page over creating a new one. If you add a new page, link it from this index in the section it belongs to.
- If a page becomes obsolete, move it to
docs/archive/rather than deleting it, so links don't rot. - Keep examples runnable — paths, commands, and config keys here should match what's actually in the repo.