
Run a live Compose app inside a miniature interactive device simulator, swap hardware live, override configuration (size, density, font scale, orientation) via a visual bezel panel.
Run your Compose app inside a simulated device and swap the hardware live. One real phone is enough.
A miniature, faithful model of a device holding your real, interactive app. Inspired by Flutter's
device_preview.
On Maven Central, Android only:
// build.gradle.kts
dependencies {
implementation("io.github.matheuslutero:diorama:<version>")
}setContent {
Diorama {
MyApp()
}
}Wrap your root and a panel lets you change the simulated device, orientation, font scale and dark mode while the app keeps running.
[!WARNING] Early skeleton. The override engine and the scaling primitive are real and verified; the device catalog and the bezel art are placeholders.
Android Studio's @Preview is design-time and per-composable. Showkase and Composium are component
browsers. Paparazzi and Roborazzi render off-device for snapshot tests. Commercial options are cloud
device farms or CI visual-diffs. None run your live app inside a simulated device on the phone in
your hand.
The correct half, a configuration-override engine, already ships as
androidx.compose.ui.test.DeviceConfigurationOverride. Diorama wires it to a runtime panel.
| Module | Targets | Holds |
|---|---|---|
:diorama-frame |
android, jvm, iosArm64, iosSimulatorArm64 | Device model, catalog, bezel, scaling primitive. Never touches Context. |
:diorama |
android | Configuration-override engine, panel, state. |
The app is constrained to the device's logical size and only scaled visually, so it reports the size
it lays out at. The override sets every axis explicitly (densityDpi, screenWidthDp, font scale)
instead of deriving density from a fit ratio the way ForcedSize does.
Simulating a 1280×800 tablet at 240dpi on a Pixel emulator:
windowSizeClass |
screenWidthDp |
densityDpi |
|
|---|---|---|---|
ForcedSize |
minW=840, minH=900 (wrong height) | 427 (host's) | 159 (wrong) |
| Diorama | minW=840, minH=480 | 1280 | 240 |
Only Diorama reports the real device. Full write-up: docs/architecture.md.
WindowInfoTracker unwraps to the real Activity, so an overridden Context
cannot fool it. Window size class simulates; posture does not.positionInWindow() returns host-window coordinates, so dropdowns and
popups can misplace.:diorama-frame is portable; :diorama needs an expect/actual seam for the
Context/Configuration half.Run your Compose app inside a simulated device and swap the hardware live. One real phone is enough.
A miniature, faithful model of a device holding your real, interactive app. Inspired by Flutter's
device_preview.
On Maven Central, Android only:
// build.gradle.kts
dependencies {
implementation("io.github.matheuslutero:diorama:<version>")
}setContent {
Diorama {
MyApp()
}
}Wrap your root and a panel lets you change the simulated device, orientation, font scale and dark mode while the app keeps running.
[!WARNING] Early skeleton. The override engine and the scaling primitive are real and verified; the device catalog and the bezel art are placeholders.
Android Studio's @Preview is design-time and per-composable. Showkase and Composium are component
browsers. Paparazzi and Roborazzi render off-device for snapshot tests. Commercial options are cloud
device farms or CI visual-diffs. None run your live app inside a simulated device on the phone in
your hand.
The correct half, a configuration-override engine, already ships as
androidx.compose.ui.test.DeviceConfigurationOverride. Diorama wires it to a runtime panel.
| Module | Targets | Holds |
|---|---|---|
:diorama-frame |
android, jvm, iosArm64, iosSimulatorArm64 | Device model, catalog, bezel, scaling primitive. Never touches Context. |
:diorama |
android | Configuration-override engine, panel, state. |
The app is constrained to the device's logical size and only scaled visually, so it reports the size
it lays out at. The override sets every axis explicitly (densityDpi, screenWidthDp, font scale)
instead of deriving density from a fit ratio the way ForcedSize does.
Simulating a 1280×800 tablet at 240dpi on a Pixel emulator:
windowSizeClass |
screenWidthDp |
densityDpi |
|
|---|---|---|---|
ForcedSize |
minW=840, minH=900 (wrong height) | 427 (host's) | 159 (wrong) |
| Diorama | minW=840, minH=480 | 1280 | 240 |
Only Diorama reports the real device. Full write-up: docs/architecture.md.
WindowInfoTracker unwraps to the real Activity, so an overridden Context
cannot fool it. Window size class simulates; posture does not.positionInWindow() returns host-window coordinates, so dropdowns and
popups can misplace.:diorama-frame is portable; :diorama needs an expect/actual seam for the
Context/Configuration half.