scivi / open source / splitflap

A departure board in React Native, played by Core Animation

October 4, 2026 · updated October 5, 2026 · react-native-splitflap

A departures board with cities in their own scripts, the four transitions, numbers, emoji, cell widths and all four directions, all moving at once
Emoji: Twemoji, CC-BY 4.0

react-native-splitflap is a React Native component that turns a change of text into a split-flap departure-board animation, in React Native and Expo: every changed letter flips, spins through its alphabet, rolls or flickers into place. It has one required prop, text, and four transitions: flip, reel, roll and scramble. This is how it is built.

npx expo install @enavermate/react-native-splitflap

Plan once in JavaScript, never run per frame

A change of text goes through a planner written in TypeScript. It splits the text into cells the way a reader sees characters — a flag, a family emoji, a keycap, a Devanagari or Thai syllable is one cell — and gives every cell its path through the alphabet, a delay, a duration, an easing and a direction. The plan is plain data. It crosses to native once, and JavaScript does nothing more until the next change. Both players take the same plan, so iOS and Android show the same animation, and a test checks that every Android cell is where the planner puts it at every instant.

iOS: a glyph atlas and four layers per cell

A board never draws text while it moves. Every glyph it can show is rasterised once into one image, the atlas, and a cell shows a glyph by moving its contentsRect over it. Atlases are cached per font, colour, spacing and scale under an 8 MB budget and dropped on a memory warning.

Each cell is a container CALayer with four glyph layers, and the plan is baked into Core Animation keyframes, so the animation runs in the render server rather than in the app.

One measurement shaped the masks: a single mask over the whole board was 3–4 fps slower on roll and reel with sixty boards on an iPhone, because any moving cell then redrew every cell under it. Each drum cell keeps its own fade.

Measured like <Text>, by a Fabric shadow node

The library is a Fabric component for one reason: its size. A C++ shadow node, shared by iOS and Android, answers Yoga's measure call. It measures each glyph through the same TextLayoutManager React Native's <Text> uses and sums them — cells are never kerned against each other — so a settled board draws every glyph exactly where a <Text> with the same style would, truncation to "…" included.

Measuring the thirty-odd letters of an alphabet on every text change cost the JS thread 2–6 fps with sixty boards, so alphabet widths are measured once per alphabet set, font and scale. That is what cell widths are built on: natural reflows like text, and uniform gives every cell one width, like a station board. Without either, each transition takes its best look — flip and scramble sort letters into narrow, regular and wide cells. Digits are tabular by default, so numbers keep their width either way.

Changes mid-flight continue, they do not restart

A new text can arrive while the board is still turning. The planner retargets from where every cell is at that moment: on iOS the player reads what each presentation layer shows, including a flap caught mid-fold at its presented angle; on Android it evaluates the old plan at the current time. The board never jumps back to the old text first.

Android: the same plan on a Canvas

The Android player follows the iOS one: the same plan, the same curves, the same four renderers. What differs is the machinery. iOS bakes each plan into keyframes; on Android every frame evaluates each cell as a pure function of time and draws it on a Canvas. Glyphs sit where the shared shadow node measured them, so a settled board matches <Text> there too.

Scripts, not languages

A cell rolls through the alphabet of its own letter's script, read from the text: Latin, Cyrillic, Greek, Armenian, Georgian, the Indic and Southeast Asian scripts, the kana and many numeral systems. Each script has one alphabet for all its languages, and a word travels only through the core letters and the extra letters it contains. Anything without an alphabet — CJK, Hangul, symbols — changes in one step, so no script can break a board.

Numbers

The same board in Swift and on the web

The iOS player was Core Animation from the start, so it stands on its own: SplitflapKit is the board as a Swift package, Splitflap(text) for SwiftUI and SplitflapView for UIKit, on iPhone, iPad and the Mac through Mac Catalyst. Its planner is a line-by-line port of the TypeScript one, and a test runs 131 scenarios through both and requires the same plans.

The TypeScript planner itself went to the web as splitflap: a <split-flap> element for HTML, Vue, Svelte and Angular, a React component and a canvas player. The picture at the top of this page is drawn from those plans, so a text animates the same way in React Native, in a native app and in a browser.

Try it

import { Splitflap } from '@enavermate/react-native-splitflap';

<Splitflap text={gate} transition="flip" surfaceColor={theme.card} />

Source, the full API reference and recipes: GitHub · npm. MIT licensed. Issues and ideas are welcome.