Getting Started

Run your first mirror

From a fresh clone to a fullscreen mirror in three commands — then open the web admin to configure it live.

On this page

Requirements

Note: the desktop app is pure JVM. Module plugins are loaded with the JDK URLClassLoader + ServiceLoader — there is no Android or iOS target.

Run the desktop app

git clone https://github.com/pierrejochem/SpeculumSmartMirror.git
cd SpeculumSmartMirror
./gradlew :composeApp:run

That single command:

  1. runs the root :deployModules task — builds every module subproject and copies its JAR into plugins/;
  2. launches the fullscreen mirror, which scans plugins/ and loads each module via reflection.

Press the OS fullscreen-exit shortcut to quit. The working directory must be the repo root so the app finds plugins/ and config.json.

Web admin (configuration)

A password-protected React UI + Ktor backend edits the mirror config (modules, positions, intervals, options). It writes the same config.json the app reads, and the running mirror hot-reloads automatically on save.

cd config-server/web && npm install && npm run build   # build the UI once
./gradlew :config-server:run                            # http://localhost:8080
Security: bootstrap password is admin. Change it in the UI's Security card (persisted as a salted PBKDF2-HMAC-SHA256 hash) or seed it with MIRROR_ADMIN_PASSWORD. In production also set MIRROR_ADMIN_SECRET (the token-signing HMAC key).

For dev mode (Vite hot reload on :5173 proxying to the backend), env vars, and the full REST API, see the User Guide and API Reference.

Project layout

mirror-api/            shared API: MirrorModule, ModuleConfig, Region,
                       MirrorColors, ModuleFactory (compiled against by all)
composeApp/            the desktop app (engine, layout, plugin loader, theme)
modules/<name>-module/  one Gradle subproject per module  ─┐ built to JAR
plugins/               deployed module JARs (scanned at runtime)  ◄┘
config-server/         web admin: Ktor backend + React/Vite UI

Add your first module

The fastest path: clone the fully-commented standalone SpeculumExampleModule, rename it, register it, add a config entry. In short:

  1. Create a modules/<name>-module/ subproject with compileOnly(project(":mirror-api")).
  2. Implement MirrorModule + a ModuleFactory, declared in META-INF/services/org.speculum.core.ModuleFactory.
  3. Register the subproject in settings.gradle.kts and the root deployModules task.
  4. Run ./gradlew :composeApp:run — your module is discovered and placed automatically.
API Reference → Developer Guide ↗