Skills DirectorySkills Directory
SkillsLearnSecurityCategoriesDocsCommunityBlog
Sign InSubmit Skill
Skills Directory

Security-tested agent skills for Claude, coding agents, and AI workflows.

Directory

  • Browse Skills
  • All Skills A–Z
  • Claude Skills
  • Claude Code Skills
  • Agent Skills
  • Categories
  • Submit a Skill

Learn

  • Learn Hub
  • Install Claude Skills
  • Write SKILL.md
  • Skills vs MCP
  • Directories Compared

Security

  • Security
  • Methodology
  • Secure Claude Skills
  • Security Badges

Company

  • About
  • Community
  • Blog
  • API Docs
  • Advertise

2026 Skills Directory. All rights reserved.

Back to skills

Flipoff Split Flap Display

ASecurity

Expert skill for building, customizing, and embedding the FlipOff split-flap display emulator — a free, offline-capable web app that turns any browser/TV into a retro airport departure board.

81 stars
0 votes
0 copies
0 views
Added 9/19/2026
devopspythongobashreactvuenodegitapisecurity

Works with

cursorterminalcliapi

Security Analysis

A100/100

Scanned 9/19/2026

Install to Claude Code

$npx -y skills add reason-machines/trending-skills --skill flipoff-split-flap-display --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Flipoff Split Flap Display?

Add the live security badge to your README — it updates automatically with every re-scan.

Security grade badge for Flipoff Split Flap Display
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/reason-machines-flipoff-split-flap-display/badge)](https://www.skillsdirectory.com/skills/reason-machines-flipoff-split-flap-display)

More formats (shields.io, HTML) on the badges page.

Download Zip
Files
SKILL.md
---
name: flipoff-split-flap-display
description: Expert skill for building, customizing, and embedding the FlipOff split-flap display emulator — a free, offline-capable web app that turns any browser/TV into a retro airport departure board.
triggers:
  - add split flap display to my site
  - customize flipoff messages or quotes
  - embed retro flip board display
  - change flipoff grid size or colors
  - how do I use flipoff split flap emulator
  - add keyboard controls to flipoff
  - flipoff sound and animation configuration
  - turn TV into flip board display
---

# FlipOff Split-Flap Display Emulator

> Skill by [ara.so](https://ara.so) — Daily 2026 Skills collection.

FlipOff is a pure vanilla HTML/CSS/JS web app that emulates classic mechanical split-flap (flip-board) airport displays. No frameworks, no npm, no build step — open `index.html` and you have a full-screen retro display with authentic scramble animations and clacking sounds.

---

## Installation

```bash
git clone https://github.com/magnum6actual/flipoff.git
cd flipoff

# Option 1: Open directly
open index.html

# Option 2: Serve locally (recommended for audio)
python3 -m http.server 8080
# Visit http://localhost:8080
```

> **Audio note:** Browsers block autoplay. The user must click once to enable the Web Audio API context. After that, sound plays automatically on each message transition.

---

## File Structure

```
flipoff/
  index.html               — Single-page app entry point
  css/
    reset.css              — CSS reset
    layout.css             — Header, hero, page layout
    board.css              — Board container and accent bars
    tile.css               — Tile styling and 3D flip animation
    responsive.css         — Media queries (mobile → 4K)
  js/
    main.js                — Entry point, wires everything together
    Board.js               — Grid manager, transition orchestration
    Tile.js                — Per-tile animation logic
    SoundEngine.js         — Web Audio API playback
    MessageRotator.js      — Auto-rotate timer
    KeyboardController.js  — Keyboard shortcut handling
    constants.js           — All configuration lives here
    flapAudio.js           — Base64-encoded audio data
```

---

## Key Configuration — `js/constants.js`

Everything you'd want to change lives in one file:

```js
// js/constants.js (representative structure)

export const GRID_COLS = 26;        // Characters per row
export const GRID_ROWS = 8;         // Number of rows

export const SCRAMBLE_DURATION = 600;  // ms each tile scrambles before settling
export const STAGGER_DELAY = 18;       // ms between each tile starting its scramble
export const AUTO_ROTATE_INTERVAL = 8000; // ms between auto-advancing messages

export const SCRAMBLE_COLORS = [
  '#FF6B35', '#F7C59F', '#EFEFD0',
  '#004E89', '#1A936F', '#C6E0F5'
];

export const ACCENT_COLORS = ['#FF6B35', '#004E89', '#1A936F'];

export const MESSAGES = [
  "HAVE A NICE DAY",
  "ALL FLIGHTS ON TIME",
  "WELCOME TO THE FUTURE",
  // Add your own here
];
```

---

## Adding Custom Messages

Edit `MESSAGES` in `js/constants.js`. Each message is a plain string. The board wraps text across the grid automatically.

```js
export const MESSAGES = [
  "DEPARTING GATE 7",
  "YOUR COFFEE IS READY",
  "BUILD THINGS THAT MATTER",
  "FLIGHT AA 404 NOT FOUND",    // max GRID_COLS * GRID_ROWS chars
];
```

**Padding rules:** Messages shorter than the grid are padded with spaces. Messages longer than the grid are truncated. Keep messages at or under `GRID_COLS × GRID_ROWS` characters.

---

## Changing Grid Size

```js
// Compact 16×4 ticker-style board
export const GRID_COLS = 16;
export const GRID_ROWS = 4;

// Wide cinema board
export const GRID_COLS = 40;
export const GRID_ROWS = 6;

// Tall info kiosk
export const GRID_COLS = 20;
export const GRID_ROWS = 12;
```

After changing grid dimensions, tiles re-render automatically on next page load.

---

## Keyboard Shortcuts

| Key | Action |
|-----|--------|
| `Enter` / `Space` | Next message |
| `Arrow Left` | Previous message |
| `Arrow Right` | Next message |
| `F` | Toggle fullscreen |
| `M` | Toggle mute |
| `Escape` | Exit fullscreen |

---

## Programmatic API

### Board

```js
// Board.js exposes a class you can instantiate directly
import Board from './js/Board.js';

const board = new Board(document.getElementById('board-container'));

// Display a specific string
board.setMessage('GATE CHANGE B12');

// Advance to next message in the rotation
board.next();

// Go back
board.previous();
```

### MessageRotator

```js
import MessageRotator from './js/MessageRotator.js';

const rotator = new MessageRotator(board, messages, AUTO_ROTATE_INTERVAL);

rotator.start();   // begin auto-advancing
rotator.stop();    // pause rotation
rotator.next();    // manual advance
rotator.previous(); // manual back
```

### SoundEngine

```js
import SoundEngine from './js/SoundEngine.js';

const sound = new SoundEngine();

// Must call after a user gesture (click/keypress)
await sound.init();

sound.play();    // play the flap transition sound
sound.mute();    // silence
sound.unmute();
sound.toggle();  // flip mute state
```

### KeyboardController

```js
import KeyboardController from './js/KeyboardController.js';

const kb = new KeyboardController({
  onNext:       () => rotator.next(),
  onPrevious:   () => rotator.previous(),
  onFullscreen: () => toggleFullscreen(),
  onMute:       () => sound.toggle(),
});

kb.attach();   // start listening
kb.detach();   // stop listening
```

---

## Embedding FlipOff in Another Page

### As an iframe

```html
<iframe
  src="/flipoff/index.html"
  width="1280"
  height="400"
  style="border:none; background:#000;"
  allowfullscreen
></iframe>
```

### Inline embed (pull in just the board)

```html
<!-- In your page -->
<div id="flip-board"></div>

<script type="module">
  import Board from '/flipoff/js/Board.js';
  import SoundEngine from '/flipoff/js/SoundEngine.js';
  import { MESSAGES, AUTO_ROTATE_INTERVAL } from '/flipoff/js/constants.js';

  const board = new Board(document.getElementById('flip-board'));
  const sound = new SoundEngine();

  let idx = 0;
  board.setMessage(MESSAGES[idx]);

  document.addEventListener('click', async () => {
    await sound.init();
  }, { once: true });

  setInterval(() => {
    idx = (idx + 1) % MESSAGES.length;
    board.setMessage(MESSAGES[idx]);
    sound.play();
  }, AUTO_ROTATE_INTERVAL);
</script>
```

---

## Custom Color Themes

```js
// js/constants.js — dark blue terminal theme
export const SCRAMBLE_COLORS = [
  '#0D1B2A', '#1B2838', '#00FF41',
  '#003459', '#007EA7', '#00A8E8'
];

export const ACCENT_COLORS = ['#00FF41', '#007EA7', '#00A8E8'];
```

```css
/* css/board.css — override tile background */
.tile {
  background-color: #0D1B2A;
  color: #00FF41;
  border-color: #003459;
}
```

---

## Common Patterns

### Show real-time data (e.g., a flight API)

```js
import Board from './js/Board.js';
import SoundEngine from './js/SoundEngine.js';

const board = new Board(document.getElementById('board'));
const sound = new SoundEngine();

async function fetchAndDisplay() {
  const res = await fetch('/api/departures');
  const data = await res.json();
  const message = `${data.flight}  ${data.destination}  ${data.gate}`;
  board.setMessage(message.toUpperCase());
  sound.play();
}

document.addEventListener('click', () => sound.init(), { once: true });
setInterval(fetchAndDisplay, 30_000);
fetchAndDisplay();
```

### Cycle through a custom message list

```js
const promos = [
  "SALE ENDS SUNDAY",
  "FREE SHIPPING OVER $50",
  "NEW ARRIVALS THIS WEEK",
];

let i = 0;
setInterval(() => {
  board.setMessage(promos[i % promos.length]);
  sound.play();
  i++;
}, 5000);
```

### React/Vue wrapper (import as a module)

```jsx
// FlipBoard.jsx
import { useEffect, useRef } from 'react';
import Board from '../flipoff/js/Board.js';
import { MESSAGES } from '../flipoff/js/constants.js';

export default function FlipBoard({ messages = MESSAGES, interval = 8000 }) {
  const containerRef = useRef(null);
  const boardRef = useRef(null);

  useEffect(() => {
    boardRef.current = new Board(containerRef.current);
    let idx = 0;
    boardRef.current.setMessage(messages[idx]);

    const timer = setInterval(() => {
      idx = (idx + 1) % messages.length;
      boardRef.current.setMessage(messages[idx]);
    }, interval);

    return () => clearInterval(timer);
  }, []);

  return <div ref={containerRef} className="flip-board-container" />;
}
```

---

## Troubleshooting

| Problem | Fix |
|---|---|
| No sound | User must click/interact first; Web Audio requires a user gesture |
| Sound works locally but not deployed | Ensure `flapAudio.js` (base64) is served; check MIME types |
| Tiles don't animate | Verify CSS `tile.css` is loaded; check for JS console errors |
| Grid overflows on small screens | Reduce `GRID_COLS`/`GRID_ROWS` in `constants.js` or add CSS `overflow: hidden` |
| Fullscreen not working | `F` key calls `requestFullscreen()` — some browsers require the page to be focused |
| Messages cut off | String length exceeds `GRID_COLS × GRID_ROWS`; shorten or increase grid size |
| Audio blocked by CSP | Add `media-src 'self' blob: data:` to your Content-Security-Policy |
| CORS error loading modules | Serve with a local server (`python3 -m http.server`), not `file://` |

---

## Tips for TV / Kiosk Deployment

```bash
# Serve with a simple static server
npx serve .              # Node
python3 -m http.server   # Python

# Auto-launch fullscreen in Chromium kiosk mode
chromium-browser --kiosk --app=http://localhost:8080

# Hide cursor after idle (add to index.html)
document.addEventListener('mousemove', () => {
  document.body.style.cursor = 'default';
  clearTimeout(window._cursorTimer);
  window._cursorTimer = setTimeout(() => {
    document.body.style.cursor = 'none';
  }, 3000);
});
```

Attribution

reason-machinesreason-machines
View sourceMore from reason-machines →
SSkills DirectorySkills Directory

Ship a skill? Prove it's safe.

Free 120-pattern security scan, letter grade, and an embeddable README badge.

Submit a skill

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments (0)

No comments yet. Be the first to comment!

SSkills DirectorySkills Directory

Ship a skill? Prove it's safe.

Free 120-pattern security scan, letter grade, and an embeddable README badge.

Submit a skill

Related Skills

Terraform Module Library

Build reusable Terraform modules for AWS, Azure, and GCP infrastructure following infrastructure-as-code best practices. Use when creating infrastructure modules, standardizing cloud provisioning, or implementing reusable IaC components.

393431 votes

sematext-otel

Wire a service's OpenTelemetry output to Sematext Cloud. Walks through region, App-type, instrumentation flow (managed OTLP endpoint vs Sematext Agent), and signal selection (traces/metrics/logs), then produces the exact env-var block and points at a runnable reference example in this repo. Invoke when instrumenting a new app for Sematext.

01 votes

Deployment Patterns

Deployment workflows, CI/CD pipeline patterns, Docker containerization, health checks, rollback strategies, and production readiness checklists for web applications. Use when setting up deployment infrastructure or planning releases.

2459130 votes

Babysit

Watch a pull request or review cycle until it is ready to merge. Use when asked to babysit, monitor, or keep checking PR comments, reviews, and CI until all actionable issues are resolved.

929660 votes

V7 Roster

Interact with the Paperclip control plane API for task coordination and governance. Use when checking assignments, updating issue status, posting comments, delegating work, managing routines, or calling Paperclip API endpoints.

798220 votes
View all in devops →