Skip to content

Repository files navigation

Squares Controller

A local-first browser controller for Twinkly Squares. It keeps the stock Twinkly firmware and coordinate map, so there is nothing to flash, open, or solder.

Version Tests License

What it can do

  • Paint, erase, fill, and preview the entire matrix in real time
  • Import still images, animated GIFs, and video with fit, sampling, saturation, contrast, gamma, and playback controls
  • Turn a browser microphone into Spectrum, Bass Halo, or 3-Band Field visuals with sensitivity, smoothing, and live frequency meters
  • Mirror a screen, window, or browser tab through the same media sampling and color-correction pipeline
  • Run sixteen procedural 2D and particle effects with speed, intensity, and optional effect-specific controls
  • Select curated palettes, build two-to-eight-stop gradients, and save custom palettes to the controller library
  • Compose two effect layers with opacity and five blend modes
  • Target the whole wall, a panel, a row, a column, or a custom rectangle; pin up to three additional 2D segments with mirroring, transpose, grouping, spacing, and offset transforms
  • Save server-persisted scenes and timed playlists with eight transitions, including cut, crossfade, push, dissolve, wipe, shift, radial, and pixelate
  • Capture up to 32 hand-built pixel frames, set each frame's hold time down to 25 ms, preview the loop, and bake it into controller movie storage
  • See real scene thumbnails, exact browser-output previews, honest controller-local playback status, active-scene state, and playlist progress
  • Organize scenes with folders, tags, favorites, search, filters, duplication, inline metadata editing, and merge-or-replace JSON backups
  • Display scrolling text, a clock, and locally loaded fonts
  • Schedule sleep, wake, brightness, stock-mode, and off actions
  • Choose safe startup and stale-browser-frame behavior; unchanged startup and hold-last-frame remain the non-disruptive defaults
  • Rotate the complete display to 0°, 90°, 180°, or 270°
  • Control live brightness while custom frames are streaming
  • Inspect advertised, measured, target, and delivered frame cadence plus relay gap, repeat, and missed-deadline telemetry
  • Bake a finite effect, video clip, or static look into unused controller movie storage for browser-free playback without overwriting existing movies
  • Integrate local tools through a versioned JSON API, OpenAPI document, CLI, Server-Sent Events, and a Home Assistant example
  • Return to the original Twinkly animation at any time

The layout is read from the Twinkly controller at startup. This project has been physically tested with a 4×3, 768-pixel Twinkly Squares wall: 32×24 at 0°/180° and 24×32 at 90°/270°.

Quick start

Requires Python 3.11 or newer. Node 18 or newer is optional; npm is only used as a convenient command runner and test runner.

cp config.example.json config.json

Edit config.json and set the private IPv4 address of your Twinkly controller:

{
  "deviceIp": "192.168.1.100"
}

Then start the app:

npm start

Or without Node:

python3 server.py

On macOS, you can also double-click scripts/start.command.

Open http://127.0.0.1:4312. Press Control-C to stop; a graceful shutdown returns the panel to its saved Twinkly animation.

Scenes, playlists, palettes, automations, and runtime policy are stored in .squares/ beside the controller. That directory and config.json are ignored by Git.

Local integrations

The versioned local API is documented in docs/INTEGRATIONS.md. A few examples:

./scripts/squaresctl status
./scripts/squaresctl brightness 25
./scripts/squaresctl rotate 270
./scripts/squaresctl off
./scripts/squaresctl stock

The live OpenAPI document is served at http://127.0.0.1:4312/openapi.json.

Configuration

Environment variables override the defaults:

Variable Purpose Default
TWINKLY_IP Twinkly controller IPv4 address config.json
HOST Web server bind address 127.0.0.1
PORT Web server port 4312
SQUARES_CONFIG Alternate configuration file ./config.json
SQUARES_LIBRARY Alternate scene/playlist file ./.squares/library.json
SQUARES_AUTOMATIONS Alternate automation file ./.squares/automations.json
SQUARES_RUNTIME_POLICY Alternate startup/frame-loss policy file ./.squares/runtime.json
ALLOW_UNAUTHENTICATED_LAN Explicitly allow a non-loopback bind unset

Start automatically on macOS

From the project directory:

./scripts/install-macos-service.sh

This installs a user LaunchAgent, starts the controller at login, and keeps it running after a crash. Remove it with:

./scripts/uninstall-macos-service.sh

Security

The server has no authentication and therefore binds only to 127.0.0.1 by default. A non-loopback bind is refused unless you make the risk explicit:

HOST=0.0.0.0 ALLOW_UNAUTHENTICATED_LAN=1 npm start

Use that only on a trusted, firewalled home network. Never port-forward port 4312 or expose it to the internet. The app also rejects public IP addresses as panel targets. See SECURITY.md.

Test

npm test

The current suite contains 49 Python and 67 browser-model tests. It covers device protocol behavior, coordinate mapping, brightness, rotation, state synchronization, persistence, scheduling, API validation, palettes, zones, blending, transitions, effects, audio analysis, live-input rendering, media controls, scene organization, playlists, pixel clips, runtime failure policy, relay telemetry, and safe movie payloads.

Frame-rate notes

The connected controller advertises 40 FPS but reports a measured 38.46 FPS clock. Realtime output therefore runs on an independent, deadline-aligned 37.5 FPS relay clock to leave narrow sampling headroom. The UI reports actual delivery gaps, repeated inputs, and missed deadlines; those host measurements do not claim that every frame lit physically. Controller-local movies use the controller's integer 38 FPS playback path and remove browser, HTTP, Python scheduling, and Wi-Fi cadence from ongoing playback. See docs/PERFORMANCE.md for the measurements.

How it works

The browser talks only to the local Python server. The server authenticates directly with the Twinkly controller over HTTP and streams RGB frames over the controller's local realtime protocol on UDP port 7777. The runtime uses the Python standard library and native browser APIs.

WLED inspiration and attribution

WLED is an excellent community-built LED firmware project. Its established product concepts—including presets and playlists, segments, palettes, transitions, effects, scheduling, and a JSON API—helped shape the roadmap for Squares Controller.

Squares Controller is an independent implementation for stock Twinkly hardware. It does not include or modify WLED firmware, source code, web UI assets, or branding, and it is not affiliated with the WLED or Twinkly projects. WLED is licensed under EUPL-1.2; Squares Controller remains MIT licensed. See NOTICE.md for the durable attribution statement.

License

MIT

About

Local-first browser controller for Twinkly Squares

Topics

Resources

Contributing

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages