A Wayland desktop that feels like someone cared. Smooth animations, thoughtful gestures, and the kind of details you notice only when they're missing — inspired by familiar macOS interactions, built from scratch in Rust.
Otto is a Wayland compositor and stacking window manager built on LayersEngine, rendered with Skia, with parts of the desktop handed straight to hardware display planes.
You can try it inside your current session in about a minute — jump to Try it.
Documentation: User Guide · Developer Guide
Testing phase. Many features are ready for daily use, but Otto is not finished and not yet fully stable. Playing with it, breaking it and telling us about it is genuinely the most useful thing you can do right now. Feedback and questions: Discord or Matrix
#otto-compositor:matrix.org.
Wallpaper, top bar, Dock, Files browsing poster thumbnails, the user guide in a browser window.
Exposé, with the workspace strip on top — the window previews are live, not screenshots.
otto-files in column view, previewing the selected image.
otto-settings editing the running compositor's configuration over D-Bus.
A dark Dock and top bar over a monochrome wallpaper.
Third-party wlr-layer-shell widgets (eww) on the desktop layer, behind windows and above the wallpaper — see Desktop Widgets.
Zero risk: Otto runs as a window inside the desktop you're using right now.
git clone https://github.com/nongio/otto
cd otto
cargo run --release # opens Otto in a window (winit backend)Then open something inside it (WAYLAND_DISPLAY=wayland-1 <your app>), minimize a window to the Dock, hit PageUp for Exposé, Ctrl+Tab for the app switcher.
For real: install a package and pick "Otto" in your login manager.
sudo dpkg -i otto_*.deb && sudo apt-get install -f # Debian / Ubuntu
sudo dnf install otto-*.rpm # Fedora / RHEL
curl -O https://raw.githubusercontent.com/nongio/otto/main/PKGBUILD && makepkg -si # ArchPackages come from the GitHub Releases page. See Installation for the details and post-install notes, and the Getting Started guide for a walkthrough.
-
A Dock that is a real task manager — pinned apps, running apps and minimized windows in one strip. Icons magnify on approach, bounce while an app is launching, auto-hide when you want the space back, and the whole thing resizes by dragging its handle.
-
Workspaces that animate — multiple workspaces per monitor, each monitor independent, drag windows between them, configurable backgrounds.
-
Exposé and an app switcher —
PageUp(or a three-finger swipe up) spreads every window out with live previews;Ctrl+Tabwalks apps, cycles windows within an app, and can close them. Both appear on the monitor under your pointer. -
Window management that stays out of the way — animated fullscreen/maximize, snap to halves, minimize to the Dock, and new windows placed where they overlap the least. Otto draws the title bar for clients that want a server-side one, on both
xdg-decorationand KDE'sorg_kde_kwin_server_decoration, so the controls keep working while an application is busy. -
A top bar and a dynamic island — clock, tray and application menus over DBusMenu; notifications, ongoing activities and permission dialogs in a floating panel; compositor-drawn volume and brightness indicators.
-
Multi-monitor that holds up — per-output rendering, hotplug, arrangement and modes from the config, virtual outputs created on demand.
-
Lock, login and power —
ext-session-lock-v1locking with a PAM-backed locker, lock on hotkey / power button / lid close / idle timeout (respectingidle-inhibit), a greetd login screen with password and fingerprint, and Otto-owned lid-suspend with clamshell awareness. -
Screen sharing — an XDG Desktop Portal backend over PipeWire: share a whole output or a single window with browsers, OBS and anything else that speaks the portal, and send an output to an AirPlay receiver. The portal also answers file-picker, screenshot and permission-dialog requests.
-
Remote desktop —
otto-rdpserves an output to any RDP client (Microsoft Remote Desktop on Windows, macOS, iOS and Android, or FreeRDP), with TLS, hardware H.264 through VA-API where the GPU offers it and a bitmap fallback where it doesn't. Remote pointer and keyboard input is injected back into the session, so it is a screen you can actually work on. -
Virtual outputs — monitors with no display behind them, declared in the config and rendered like any other screen, each published as a PipeWire node. They have their own workspaces, exposé and workspace selector, and you can drag windows onto them: a remote screen for RDP, a stage to record or cast from, or simply more desk than your hardware has.
-
X11 apps, including fullscreen games — keyboard focus for globally-active clients, output scale via XSETTINGS, direct scanout.
-
A file manager —
otto-files: list, icon and column views, thumbnails read from the shared cache other file managers write, drag and drop in and out of other apps, background copy/move/trash with progress and 32 levels of undo, and a quick-view panel onSpacethat previews pictures, text, PDFs and media details in a sandboxed process. It is also the desktop's file picker, so Open and Save dialogs in Firefox and Chrome are this window rather than a GTK one. -
A settings app —
otto-settingsedits the configuration live over D-Bus, so you don't have to hand-write TOML (you still can; the compositor picks up file edits). Displays, Dock, input, sound, power, lock and login, and the shortcut list. -
A launcher —
otto-launcher:Ctrl+Spaceto start an application,Ctrl+Shift+Pto jump to a window, with fuzzy ranking and arithmetic in the query field. All three are first versions — they aim to be useful day to day, and each guide lists what is still missing rather than leaving you to find out. -
Rendering built for this — a Skia pipeline with KMS multi-plane scanout (Dock, app switcher, popups and topmost windows on their own hardware planes) and cross-plane backdrop blur.
-
Input and theming — natural and two-finger scrolling, keyboard remapping, fully configurable shortcuts, dark/light themes, accent colors, night shift through hardware gamma tables.
Note on KMS scanout: on the tty-udev backend, Otto puts parts of the desktop on their own hardware planes instead of compositing everything into one buffer, keeping the number of overlapping planes small to limit GPU work. This has mostly been tested on Intel GPUs. Other drivers are expected to fall back to full composition when the atomic test rejects a plane configuration, but that path is untested — if you see missing, misplaced or flickering elements on AMD or NVIDIA, this is the first thing to suspect, and a report is welcome. See docs/developer/drm_plane.md.
- Screen capture: a screenshot UI for picking a region or a window interactively, and per-window capture through
wlr-screencopy. Whole-output and region capture already work withgrim, the desktop portal answers screenshot requests from applications, and per-window capture is available through the screen-sharing portal. - Multi-monitor: display mirroring.
- Dock: favorite locations; moving Dock code out of the compositor core.
- Input: scroll acceleration.
- Scene graph protocol: a WIP protocol (otto-surface-style-unstable-v1) exposing the scene graph and its animations to clients — size, position, corner radius, blur and shadow driven by compositor-side springs, a Core Animation-like model. The top bar and the dynamic island are built on it.
Otto implements a comprehensive set of protocols — click to expand
- Core:
wl_compositor,wl_subcompositor,wl_shm,wl_seat,wl_data_device_manager - Shells:
xdg_wm_base(XDG shell),xdg_decoration_manager_v1,org_kde_kwin_server_decoration,wlr_layer_shell_v1(Layer shell 1.0),xwayland_shell_v1 - Output management:
wl_output,xdg_output,wp_presentation,wp_fractional_scale_v1,wp_viewporter - Rendering and DRM:
zwp_linux_dmabuf_v1,wp_linux_drm_syncobj_v1(explicit sync),wp_drm_lease_device_v1 - Input: pointer gestures, relative pointer, pointer constraints, tablet,
wp_cursor_shape_v1, keyboard shortcuts inhibit, text input, input method, virtual keyboard,zwlr_virtual_pointer_v1, XWayland keyboard grab - Selection: primary selection, data control (wlr-data-control)
- Session:
ext_session_lock_v1,zwp_idle_inhibit_manager_v1,xdg_activation_v1, security context - Window listing:
ext_foreign_toplevel_list_v1,zwlr_foreign_toplevel_management_v1 - Capture:
zwlr_screencopy_v1 - XDG foreign: cross-client surface identification
- Display control:
zwlr_gamma_control_v1(color temperature / night shift with hardware gamma tables) - Otto extensions:
otto-surface-style-unstable-v1,otto-dock-v1
For where each one is implemented and how to trace it through the code, see docs/developer/wayland.md.
Pre-built packages are on the GitHub Releases page.
# Download the .deb package from releases, then:
sudo dpkg -i otto_*.deb
sudo apt-get install -f # Install dependencies if needed# Download the .rpm package from releases, then:
sudo dnf install otto-*.rpm
# or
sudo rpm -i otto-*.rpm# Download PKGBUILD and let makepkg fetch the tarball automatically:
curl -O https://raw.githubusercontent.com/nongio/otto/main/PKGBUILD
makepkg -siIf you already downloaded the tarball from GitHub Releases, put the PKGBUILD in the same directory — makepkg will use it without re-downloading:
cd ~/Downloads # wherever your otto-*-x86_64.tar.gz is
curl -O https://raw.githubusercontent.com/nongio/otto/main/PKGBUILD
makepkg -siOtto appears in your login manager (GDM, SDDM, LightDM, …) as "Otto" in the session menu. Select it and log in.
- The packages install
/etc/otto/config.tomland never overwrite a modified one on upgrade. Edit it to configure the whole machine, or copy it to~/.config/otto/config.tomlfor just your user. On a HiDPI display setscreen_scale = 2.0. If that file is missing, Otto falls back to compiled-in defaults and you get an empty Dock with no pinned apps. - Screen sharing requires
xdg-desktop-portalon your system. - Using Otto as the login screen (
otto --loginwithotto-greeter) requiresgreetd. On Debian/Ubuntu, copy the shippedotto-lock.pamexample to/etc/pam.d/otto-lockbefore using the screen locker — the Arch and Fedora packages install it for you. - Otto handles the lid switch and the power button itself. Set
HandleLidSwitch=ignoreandHandlePowerKey=ignoreinlogind.conffor those to work.
Install these (package names vary by distribution):
libwaylandlibxkbcommonlibudevlibinputlibgbmlibseat
Add xwayland if you want to run X11 applications inside Otto. Minimum supported Rust is 1.87.0 for the compositor; building the whole workspace needs 1.96.0 (otto-rdp pins it through GStreamer).
git clone https://github.com/nongio/otto
cd otto
# Run Otto (auto-detects backend)
cargo run --release
# Development features (scene debugger, profiler)
cargo run --features "dev"Otto picks the backend for your environment:
- inside a Wayland session it uses
--winit(runs as a window) - in a TTY it uses
--tty-udev(bare metal display)
Force a backend by passing it as an argument:
--tty-udev: start Otto in a tty withudevsupport — the "traditional" launch of a Wayland compositor. May require root if your system has nologind.--winit: start Otto as a Winit application, inside another X11 or Wayland session. Best for development.--x11: start Otto as an X11 client. Quite basic and not really maintained.
Otto reads TOML configuration files, in this order (later files override earlier ones):
- System:
/etc/otto/config.toml(not shipped; copy it from/etc/otto/config.example.toml, which the packages do install) - User:
$XDG_CONFIG_HOME/otto/config.toml(defaults to~/.config/otto/config.toml) - Local override:
./otto_config.toml(current directory, for development) - Backend-specific:
./otto_config.{backend}.toml(highest priority)
otto_config.example.toml is a complete, commented example:
mkdir -p ~/.config/otto
cp otto_config.example.toml ~/.config/otto/config.toml
$EDITOR ~/.config/otto/config.tomlOr skip the editor and use the otto-settings app, which changes settings live and writes them back to the same file.
Files named otto_config.{backend}.toml in the current directory — otto_config.winit.toml, otto_config.udev.toml — override everything else. Handy when, say, you want a different screen_scale in a window than on bare metal.
Every hotkey is configurable in the [keyboard_shortcuts] section:
[keyboard_shortcuts]
"Ctrl+Return" = { open_default = { role = "terminal", fallback = "foot" } }
"Ctrl+Space" = { run = { cmd = "otto-launcher", args = [] } }
"Logo+Space" = { open_default = "file_manager" }
"Logo+B" = { open_default = "browser" }
"Ctrl+1" = { builtin = "Workspace", index = 0 }
"Ctrl+Tab" = "ApplicationSwitchNext"
"Prior" = "ExposeShowAll"For everything else — window management, workspaces, gestures, the Dock and top bar, screen sharing, remote desktop, locking and login — see the configuration reference and the User Guide.
Otto is the compositor plus a set of components, each under components/ and buildable on its own with cargo build -p <name>:
| Component | Description |
|---|---|
otto |
Main compositor binary |
otto-bar |
Top bar: clock, tray and application menus |
otto-islands |
Dynamic island: notifications, activities and dialogs |
otto-lock |
PAM-backed screen locker (ext-session-lock-v1) |
otto-greeter |
Login screen client speaking greetd's IPC |
otto-auth-ui |
Authentication panel shared by the locker and the greeter |
otto-settings |
Settings app, driving the compositor over D-Bus |
otto-files |
File manager, and the desktop's file picker |
otto-quickview |
Sandboxed preview decoder behind Files' quick view |
otto-launcher |
Keyboard-driven launcher — type to filter apps and windows |
otto-rdp |
RDP bridge serving a virtual output to a remote client |
otto-kit |
UI toolkit the Otto clients are built on |
xdg-desktop-portal-otto |
XDG Desktop Portal backend: screen sharing, file picker, screenshots, settings, permission dialogs |
apps-manager |
Debug tool for ext_foreign_toplevel_list_v1 |
To exercise a component against a running compositor:
cargo run --release -- --winit &
WAYLAND_DISPLAY=wayland-1 cargo run -p otto-launcherThe Developer Guide covers architecture, the rendering pipeline, the render loop, the scene graph, layers, DRM planes, screen sharing and more. The same pages live in docs/developer/.
Otto can profile itself with puffin, through the profile feature (also enabled by dev):
- Run the compositor with profiling on — the puffin HTTP server starts on port 8585:
cargo run --features "profile" -- --winit - Install
puffin_viewer:cargo install puffin_viewer
- Connect it to
127.0.0.1:8585.
You get frame timing, render performance and other metrics for finding bottlenecks.
Note: your puffin_viewer version must match the puffin version Otto uses (0.19.x needs puffin_viewer 0.22.0 or later).
Otto and LayersEngine are both open to contributions — test the compositor, report bugs, implement features, bring ideas. Questions and bug reports go to the issue tracker, the Discord server or the Matrix room.
The repository ships AGENTS.md, automated code review instructions and developer documentation, for human contributors and coding agents alike.
MIT. See LICENSE.
- Icons used: Fluent Icon Theme
- Font used: Inter Font
- Background used: Zach Lieberman Soft Circle Study #6 2024 zach.li





