A Kodi screensaver that renders a mechanical split-flap (Solari) departure board showing phrases, the time, and the weather.
Characters flap forward through a drum exactly as the hardware does — at the real 200ms-per-flap rate, so a clock's minutes digit spins on every rollover while its neighbour ticks once.
⬇ repository.splitflap-1.0.0.zip — install this one file and everything else follows.
Settings → Add-ons → Install from zip file → pick that zip. Then Settings → Add-ons → Install from repository → Split-Flap Repository → Look and feel → Screensaver → Split-Flap Board.
Finally Settings → Interface → Screensaver → Split-Flap Board.
Installing the repository rather than the screensaver directly is worth the extra step: the screensaver and any glyph packs then update themselves through Kodi's normal add-on browser, which matters on a Fire TV or webOS box where there is no comfortable way to copy a file onto the device again.
If you would rather Kodi fetch the repository zip itself — Settings → File
manager → Add source → <None> → Add network location. Kodi splits
the URL across fields rather than taking it whole:
| Field | Value |
|---|---|
| Protocol | Web server directory (HTTPS) |
| Server address | asm0dey.github.io |
| Remote path | kodi-splitflap/repo |
| Port | 443 |
Leave username and password empty. That builds
https://asm0dey.github.io:443/kodi-splitflap/repo, which lists the four
add-on directories. Name the source splitflap, then Install from zip file
→ splitflap → repository.splitflap/.
Direct downloads, no updates:
- screensaver.splitflap — the screensaver
- resource.images.splitflap.nimbus-ru — Cyrillic glyph pack
- script.splitflap.source.recentlyadded — Recently Added source add-on
Or build one yourself with tools/build_addon.sh (see Development below).
Fire TV users: Fire OS runs its own screensaver on its own timer. If Kodi's screensaver timeout is longer than Fire OS's, Amazon's photo screensaver activates first and Kodi's never gets a turn. Set Kodi's screensaver timeout (Settings → Interface → Screensaver → Wait time) shorter than Fire OS's (Settings → Display → Screensaver → Start Time) so Split-Flap Board actually wins the race.
| Setting | Meaning |
|---|---|
| Rows | Board height. Columns derive from it — the default 6 gives 22 columns, matching a real board. |
| Seconds per board | How long a finished board is held. Counted from when the flap finishes. |
| Maximum flap steps | Caps how many drum positions a cell steps through per transition, so a long jump around the drum doesn't take forever to settle. |
| Letter colour (RRGGBB) | Tile lettering colour. |
| Accent colour (RRGGBB) | Colour used for accented cells (e.g. an author line, a corner marker). |
| Animate the falling card | Draws the card itself falling through the hinge, rather than only the two faces it moves between. Costs a faster render loop while a board is in transition; turn it off on a slow box. |
| Show | What the board displays: live info, phrases, or an installed contributor add-on (see "Writing a source add-on" below). One at a time — they never interleave. |
| Time / Date / Weather / Now playing | Which live-info fields are shown, when Show is set to live info. |
| Combine onto one board | Whether the enabled live-info fields share one board, or rotate across separate boards. |
| Phrase file | One phrase per board. # comments and blank lines are ignored, \n puts the author on its own line. |
| Phrase URL | Optional remote phrase list, merged into the same pool as the phrase file. Fetched on a background thread; falls back to a disk cache if the fetch fails. |
| Glyph pack | Add-on id of an installed glyph pack, for non-Latin scripts or a different typeface. |
| Source add-on | When Show is set to Add-on, this button lists the installed source add-ons by name — pick one, nothing to type. Left unset, the screensaver uses whichever contributor it finds; an uninstalled or missing one falls back to live info. |
| Configure source add-on | Opens the picked add-on's own settings. |
# Lines starting with a hash are ignored.
THE ONLY WAY OUT IS THROUGH\nROBERT FROST
KEEP GOING
One phrase per line. A literal \n inside a line splits it onto multiple
board lines — used to put an attribution/author on its own line below the
quote, as in the first example above. Blank lines and lines starting with
# are skipped.
The bundled glyphs cover capitals-only extended ASCII — Western European out of the box. Anything else shows as tofu (□) until you install a pack.
Build one on a desktop with Python and Pillow:
python tools/make_glyph_pack.py --font YourFont.ttf \
--charset ascii,cyrillic \
--id resource.images.splitflap.yourfont-ru \
--name "Split-Flap — YourFont, Latin + Cyrillic" \
--out pack.zipInclude ascii in the letterset unless you want the board mixing two
typefaces. Named charsets currently available: ascii, cyrillic,
greek, hebrew. You can also pass literal characters with --chars or
read them from a file with --chars-from.
The result is a standard kodi.resource.images addon zip — install it like
any other addon (Install from zip file), then set the Glyph pack setting to
its id.
Arabic and Devanagari are out of scope: a cell grid cannot render connected scripts. Real split-flap hardware has the same limitation.
A bundled source add-on, script.splitflap.source.recentlyadded, boards the
newest additions to Kodi's video library — one item per board:
█ THE BEAR
█ S03E04 HONEYDEW
Movies and episodes are merged into one newest-first list and the top N kept, with no per-type quota: if the last twenty things added were all episodes, you get twenty episode boards. A movie's second line is its year, an episode's is its season/episode code and title. Both lines carry an accent marker.
Install it from the repository, then set Show to Add-on. Its own settings (Add-ons → My add-ons → ...) are:
| Setting | Meaning |
|---|---|
| Seconds per board | How long each item is held. Independent of the screensaver's own hold — it can be longer or shorter. |
| How many recent items to cycle | Top N, newest first. Re-queried when the cycle wraps. |
An empty library boards NO RECENT ADDITIONS; a library query that fails keeps
showing the last items it did get.
A source add-on supplies board content; this add-on renders it. Give it an
id under script.splitflap.source., declare both extension points, and
expose create_source() from source.py:
<!-- module: how source.py is imported. script: what makes Kodi's add-on
browser show it -- pure modules are hidden as dependencies. -->
<extension point="xbmc.python.script" library="default.py">
<provides>executable</provides>
</extension>
<extension point="xbmc.python.module" library="."/># script.splitflap.source.quotes/source.py
class QuoteSource:
id = "script.splitflap.source.quotes"
def next(self):
return {
"lines": ["THE ONLY WAY OUT", "IS THROUGH"],
"accents": [{"corner": "top-left"}],
"refresh_in": None,
}
def create_source():
return QuoteSource()next() is called when the board's hold expires, or after refresh_in
seconds, whichever comes first. Return refresh_in: None if your content
only changes when asked.
Return any object with a next() method; a plain dict with lines,
accents and refresh_in works. Accents are positioned relatively —
{"before_line": n}, {"corner": "top-left"} — or explicitly with
{"cell": [row, col]}.
Want your editor to check that shape? Copy this into your own add-on — it is stdlib only, so it adds no dependency on this one:
from typing import Literal, Sequence, TypedDict
class Accent(TypedDict, total=False):
before_line: int
corner: Literal["top-left", "top-right", "bottom-left", "bottom-right"]
cell: Sequence[int]
class ContentDict(TypedDict, total=False):
lines: Sequence[str]
accents: Sequence[Accent]
refresh_in: float | NoneEvery key is optional; anything you leave out gets the same default an omitted argument would.
Keep next() fast. It runs on the render loop, and a source that raises is
disabled for the session while a source that hangs freezes the
screensaver.
No dependency declaration is needed on either side — this add-on discovers
any installed, enabled add-on whose id starts with
script.splitflap.source. and calls its create_source(). Set Show to
Add-on and pick it with the Source add-on button, which lists every
installed contributor by name. A contributor that fails to import, has no
create_source(), or whose next() raises is skipped and logged — one
broken contributor never hides the others, and an uninstalled or missing
contributor falls back to live info.
pip install -e ".[dev]"
python -m pytest
python tools/build_bundled.py # regenerate bundled glyphs
./tools/build_addon.sh # packageThe core is pure Python with no Kodi imports and is fully unit-tested. The
Kodi shell (board.py, liveinfo.py, config.py, default.py) is verified
by hand on a device — see docs/superpowers/spikes/.
GPL-2.0-or-later. Bundled glyphs are rendered from Nimbus Sans (AGPLv3 with
font exception) — see assets/fonts/LICENSE-nimbus.txt.
Kodi refuses an install-from-zip whose version is not newer than the one
already installed, so a rebuilt zip at the same version silently fails to
install. Build with --bump and each zip gets the next patch version, so it installs
over the previous one:
./tools/build_addon.sh --bump # 0.1.1 -> 0.1.2 -> 0.1.3 ...
./tools/build_addon.sh # rebuild at the current version
python3 tools/bump_version.py minor # or major, when it is a real releaseTo reinstall the same version instead, remove the add-on first:
# or, to reinstall the same version
rm -rf ~/.kodi/addons/screensaver.splitflap # desktop
adb shell pm clear org.xbmc.kodi # Fire TV: nuclear, wipes settingsOn a Fire TV the gentler route is Settings → Add-ons → My add-ons → Screensavers → Split-Flap Board → Uninstall, then install the new zip.
