Dress Emacs in a Wayland skin.
emskin wraps Emacs inside a nested Wayland compositor so that any program — browsers, terminals, video players, etc. — can be embedded into Emacs windows as if they were native buffers.
- Embed any program — Wayland and X11 apps alike, including FPS games / browser Pointer Lock (pointer constraints + raw mouse delta)
- Window mirroring — display the same app in multiple Emacs windows
- Input method support — shares the host IM with precise cursor positioning
- Clipboard sync — bidirectional between host and embedded apps
- Launcher support — rofi / wofi / zofi work out of the box
- Automatic focus management — new windows auto-focus; focus falls back on close
- Built-in screen recording & screenshots — toggle MP4 capture or snap a PNG, no external tools
| Desktop | Wayland | X11 |
|---|---|---|
| GNOME | ✓ | ✓ |
| KDE | ✓ | ✓ |
| Sway | ✓ | — |
| COSMIC | ✓ | — |
pgtk Emacs (--with-pgtk) is recommended. GTK3 X11 Emacs also works via XWayland.
Requires Rust ≥ 1.89 (rust-toolchain.toml pins 1.92.0). If your distro ships an older rustc, install via rustup:
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | shyay -S emskin-bin# Dependencies (Arch Linux)
sudo pacman -S wayland libxkbcommon mesa
# Option 1: cargo install
cargo install --git https://github.com/emskin/emskin.git
# Option 2: build from source
git clone https://github.com/emskin/emskin.git
cd emskin && cargo build --release# Zero-config: auto-loads built-in elisp, no Emacs setup needed
emskin --standaloneInside Emacs running in emskin:
M-x emskin-open-native-app RET firefox
M-x emskin-open-native-app RET foot
The app embeds into the current Emacs window and receives keyboard focus.
When an embedded app has focus, keystrokes go directly to it. Emacs prefix keys (C-x, C-c, M-x) are intercepted and sent back to Emacs; focus restores automatically after the key sequence completes.
C-x o— switch Emacs windows (embedded apps follow buffer switches)C-x 1/C-x 2/C-x 3— normal window operations; embedded apps resize automatically
emskin ships five live-toggleable effects, plus a non-toggleable startup splash.
| Effect | Variable | Toggle | What it does |
|---|---|---|---|
| measure | emskin-measure |
M-x emskin-toggle-measure |
Figma-style pixel inspector: crosshair, coordinates, rulers |
| skeleton | emskin-skeleton |
M-x emskin-toggle-skeleton |
Frame-layout wireframes (debug overlay, clickable labels) |
| cursor trail | emskin-cursor-trail |
M-x emskin-toggle-cursor-trail |
Elastic spring trail behind the mouse pointer |
| jelly cursor | emskin-jelly-cursor |
M-x emskin-toggle-jelly-cursor |
Jelly-style animation on Emacs's text caret (pgtk-only color sync) |
| recorder | emskin-record |
M-x emskin-toggle-record |
MP4 screen capture with on-screen indicator (red dot + MM:SS timer) |
All default to off. Configure in ~/.emacs.d/init.el:
(setq emskin-cursor-trail t
emskin-jelly-cursor t)Values sync automatically on IPC connect, so setq works unchanged. After changing a variable mid-session, run M-x emskin-apply-config to push it immediately.
Two independent commands; either one works while the other is active:
| Command | Output | Customize |
|---|---|---|
M-x emskin-toggle-record |
~/Videos/emskin/emskin-YYYYMMDD-HHMMSS.mp4 |
emskin-record-dir, emskin-record-fps (default 30) |
M-x emskin-screenshot |
~/Videos/emskin/emskin-YYYYMMDD-HHMMSS.png |
emskin-screenshot-dir (defaults to emskin-record-dir) |
The recorder is also exposed as a regular toggle (above), so it picks up the same setq + emskin-apply-config lifecycle as the other effects. Bind to your key of choice — for example:
(global-set-key (kbd "C-c C-r") #'emskin-toggle-record)Each Emacs frame maps to a workspace:
C-x 5 2— create workspaceC-x 5 o— switch workspaceC-x 5 0— close current workspace
A top-anchored workspace bar (emskin-bar) appears automatically once a
second workspace exists and disappears when you drop back to one. Control it
via --bar=<mode> on the emskin CLI:
--bar=auto(default) — findemskin-barnext to the emskin binary, falling back toPATH--bar=none— don't launch a bar (e.g. you run waybar yourself)--bar=/path/to/binary— launch a custom bar instead (anything speakingzwlr-layer-shell-v1 + ext-workspace-v1, such as waybar with the right modules)
The bar is a standalone Wayland client — it never talks to emskin's private IPC, only standard Wayland protocols — and its lifecycle follows the compositor: it starts when emskin starts and exits when the Wayland socket closes.
Bind a key to launch rofi / zofi:
;; zofi — a launcher designed for emskin, see https://github.com/emskin/zskins
(defun my/emskin-zofi ()
(interactive)
(start-process "zofi" nil "setsid" "zofi"))
(global-set-key (kbd "C-c z") #'my/emskin-zofi)
;; rofi
(defun my/emskin-rofi ()
(interactive)
(start-process "rofi" nil
"setsid" "rofi"
"-show" "combi"
"-combi-modi" "drun,ssh"
"-terminal" "foot"
"-show-icons" "-i"))
(global-set-key (kbd "C-c r") #'my/emskin-rofi)Without --standalone, load the elisp manually:
(add-to-list 'load-path "/path/to/emskin/elisp")
(require 'emskin)emskin [OPTIONS]
--standalone Standalone mode: auto-load built-in elisp
--no-spawn Don't start Emacs; wait for external connection
--command <CMD> Program to launch (default: "emacs")
--arg <ARG> Arguments for --command (repeatable)
--bar <MODE> Workspace bar: "auto" (default), "none", or a path
--xkb-layout <LAYOUT> Keyboard layout (e.g. "us", "cn")
emskin supports software rendering (llvmpipe), but older Mesa (< 21.0) may crash at high resolutions:
# Check renderer
glxinfo | grep "OpenGL renderer"
# If llvmpipe at high resolution, reduce it
xrandr --output Virtual-1 --mode 1920x1080Make sure mesa is installed: sudo pacman -S mesa mesa-utils (Arch) or sudo apt install mesa-utils (Debian/Ubuntu).
- Smithay — the Rust Wayland compositor library emskin is built on.
- holo-layer — the jelly text-cursor effect and the elisp caret-tracking pattern (
post-command-hook+pos-visible-in-window-p) are adapted from holo-layer. Thanks to @manateelazycat.
GPL-3.0

