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.
Requirements
- JDK 17–21. The Gradle wrapper (Gradle 8.9) runs on JDK 17–21. If your default
javais newer, pointorg.gradle.java.home(in your user~/.gradle/gradle.properties) orJAVA_HOMEat a 17/21 JDK. - Node 18+ & npm — only needed to build the web admin UI.
- IntelliJ IDEA (recommended) or just the Gradle CLI.
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:
- runs the root
:deployModulestask — builds every module subproject and copies its JAR intoplugins/; - 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
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:
- Create a
modules/<name>-module/subproject withcompileOnly(project(":mirror-api")). - Implement
MirrorModule+ aModuleFactory, declared inMETA-INF/services/org.speculum.core.ModuleFactory. - Register the subproject in
settings.gradle.ktsand the rootdeployModulestask. - Run
./gradlew :composeApp:run— your module is discovered and placed automatically.