A Node.js project for controlling and simulating flipdot displays, perfect for educational purposes and creative coding exercises.
This project provides a framework for generating animations for flipdot displays, which are electromechanical displays consisting of small discs (dots) that can be flipped to show different colors (typically black or white). The application:
- Creates bitmap graphics on a virtual canvas
- Processes these graphics for flipdot compatibility
- Provides a real-time web preview
- Outputs frames as PNG images
The board displays the "lives touched" counter from
https://observatory.owow.dev/lives-touched
(plain text, e.g. 24) under a LIVES TOUCHED caption.
- The number is fetched on start-up and refreshed every 15 minutes (
REFRESH_INTERVAL). - A failed fetch is retried after 30 seconds (
RETRY_INTERVAL) and keeps the last known number on the board;--is shown only until the first fetch succeeds. - The canvas is only redrawn when the number changes, so the dots stay put in between.
- Between 1,000,000,000 and 1,000,100,000 (
CELEBRATION_RANGE) the board drops the counter and shows1 BILLIONwith confetti around it. - Crossing into that range - a fetch below a billion followed by one inside it - twinkles the
confetti once per second (
TWINKLE_INTERVAL) for at most a minute (TWINKLE_DURATION). After that the starfield holds still and the board is back to changing only when the number does. Any other in-range value shows the message without moving a dot, so a restart long after the milestone does not throw a second party.
Endpoint, caption and timings live in src/settings.js.
Make sure you have Node.js installed.
Clone the repository and install dependencies:
git clone <repository-url>
cd node-flipdots
npm installStart the development server with:
npm run devThis runs the application with nodemon for automatic reloading when files are modified.
Once running:
- Open your browser and navigate to
http://localhost:3000/view - You'll see the real-time preview of the flipdot display output
src/index.js- Main entry point that sets up the canvas, rendering loop, and example animationssrc/lives-touched.js- Fetches the number from the observatory endpoint and keeps it freshsrc/celebration.js- The1 BILLIONscreen: message plus twinkling confettisrc/ticker.js- Handles the timing mechanism (like requestAnimationFrame for Node.js)src/preview.js- Creates a simple HTTP server for real-time preview in the browsersrc/settings.js- Configuration for display resolution, panel layout, and framerateoutput/- Directory containing generated PNG frames
The display settings can be modified in src/settings.js:
export const FPS = 15; // Frames per second
export const PANEL_RESOLUTION = [28, 14]; // Size of each panel in dots
export const PANEL_LAYOUT = [3, 2]; // Layout of panels (horizontal, vertical)
export const RESOLUTION = [ // Total resolution calculation
PANEL_RESOLUTION[0] * PANEL_LAYOUT[0],
PANEL_RESOLUTION[1] * PANEL_LAYOUT[1],
];The main rendering loop is in src/index.js. To create your own animations:
- Modify the callback function in the
ticker.start()method - Use the canvas 2D context (
ctx) to draw your graphics - The graphics are automatically converted to black and white for the flipdot display
Example of drawing a simple animation:
ticker.start(({ deltaTime, elapsedTime }) => {
// Clear the canvas
ctx.clearRect(0, 0, width, height);
ctx.fillStyle = "#000";
ctx.fillRect(0, 0, width, height);
// Draw something (e.g., moving circle)
const x = Math.floor(((Math.sin(elapsedTime / 1000) + 1) / 2) * width);
ctx.fillStyle = "#fff";
ctx.beginPath();
ctx.arc(x, height/2, 5, 0, Math.PI * 2);
ctx.fill();
});The application automatically converts all drawn graphics to pure black and white using thresholding:
// Any pixel with average RGB value > 127 becomes white, otherwise black
const brightness = (data[i] + data[i + 1] + data[i + 2]) / 3;
const binary = brightness > 127 ? 255 : 0;The rendered frames are saved as PNG files in the output directory and can be accessed via the web preview or directly from the filesystem.
Some ideas to extend this project:
- Add text scrolling animations
- Implement Conway's Game of Life
- Create a clock or countdown timer
- Add socket.io for remote control
- Create a library of animation effects
- Build an API to control the display