halo is a curiosity-driven hobby package and experimental Emacs minor mode
for quiet editing interfaces. It keeps the current line and nearby lines at
normal contrast, then gradually reduces the contrast of visible lines farther
away from point.
The goal is to reduce eye movement by keeping the cursor near the vertical center, and to make the center easier to attend to by keeping it at higher contrast than the surrounding lines.
This may help improve focus, but that is only an informal expectation.
- Emacs 29.1 or later
Place this directory somewhere in load-path, then load it:
(add-to-list 'load-path "~/.emacs.d/lisp/halo.el")
(require 'halo)Enable it in a buffer with:
(halo-mode 1)Or run:
M-x halo-mode
Enable it globally with:
(halo-global-mode 1)Or run:
M-x halo-global-mode
Minimal global setup:
(use-package halo
:load-path "~/.emacs.d/lisp/halo.el"
:config
(halo-global-mode 1))With explicit settings:
(use-package halo
:load-path "~/.emacs.d/lisp/halo.el"
:config
(halo-global-mode 1)
:custom
(halo-radius 12)
(halo-min-alpha 0.360)
(halo-steps 6)
(halo-falloff 'smoothstep)
(halo-idle-delay 0.06)
(halo-live-update t)
(halo-center-cursor t)
(halo-center-fraction 0.5)
(halo-virtual-top-margin t)
(halo-global-excluded-modes '(minibuffer-mode))
(halo-global-exclude-predicate nil))halo-radius: lines above and below point that stay at normal contrast.halo-min-alpha: minimum foreground alpha for far-away lines.halo-steps: number of contrast levels.halo-falloff: contrast curve outside the focus radius. The defaultsmoothstepkeeps the edge of the focus area gentler than linear dimming.halo-idle-delay: idle delay before overlays are refreshed.halo-live-update: when non-nil, update overlays immediately after cursor movement.halo-center-cursor: when non-nil, callrecenterafter cursor movement so point stays near the vertical center of the selected window.halo-center-fraction: vertical resting position for point when centering is enabled.0.5is the middle of the window; slightly smaller values leave more preview context below point.halo-virtual-top-margin: when non-nil, add display-only space before the first buffer line so centering also works at the beginning of the buffer.halo-global-excluded-modes: major modes wherehalo-global-modeshould not enablehalo-mode. Derived modes are also excluded.halo-global-exclude-predicate: optional predicate called beforehalo-global-modeenableshalo-modein a buffer.
Refresh the selected window manually with:
M-x halo-refresh
Refresh all live windows showing buffers where halo-mode is enabled with:
M-x halo-refresh-all
For an even wider high-contrast area:
(setq halo-radius 16)
(setq halo-min-alpha 0.55)This package intentionally starts as a stable MVP.
- It is a buffer-local minor mode.
halo-global-modeenables the buffer-local mode in ordinary buffers and skips minibuffers by default.- It can update immediately after commands, so contrast follows fast cursor
movement. Idle refresh remains available when
halo-live-updateis nil. - It only processes the selected window’s visible range.
- Contrast distance is measured in displayed lines, so folded or invisible sections do not make nearby visible lines look artificially far away.
- The default contrast curve is smoothstep rather than linear, so lines just outside the focus radius do not drop abruptly.
- For folded buffers such as Magit, the visible display lines are scanned once per refresh and then reused for distance calculation.
- It caches dimming face specs and applies them to visible text ranges, keeping syntax foreground hues softened instead of flattening a dimmed line to one color.
- It removes old contrast overlays before every refresh.
- It also removes orphan overlays tagged by this package, so stale dimming does not survive reloads or mode re-enabling.
- The current line and the configured radius around point are left untouched.
- Optional cursor centering uses Emacs’ built-in
recenter. - The beginning of the buffer can receive a display-only virtual top margin, so the first line can still sit near the vertical center without inserting text.
The dimming face blends each visible text range’s foreground color toward the default background. This keeps syntax colors recognizable while reducing contrast away from point.
- Overlay faces from other packages may not always be reflected in the softened foreground color.
- If the same buffer is shown in multiple windows, each halo overlay is scoped to the window it was created for.
- Very unusual themes or terminal color setups may produce less smooth dimming.
- The package does not try to dim non-selected windows yet.
For prose-heavy buffers, a slightly larger focus radius is often more readable:
(add-hook
'org-mode-hook
(lambda ()
(setq-local halo-radius 5)
(setq-local halo-min-alpha 0.45)
(setq-local halo-center-cursor t)
(halo-mode 1)))
(add-hook
'markdown-mode-hook
(lambda ()
(setq-local halo-radius 4)
(setq-local halo-min-alpha 0.42)
(setq-local halo-center-cursor t)
(halo-mode 1)))- A companion mode that dims buffers in non-selected windows.
- Theme-aware color blending that handles more unusual face combinations.
- A child-frame mask backend for GUI-only experiments that dim by covering distant screen regions instead of changing text foregrounds.
- Separate profiles for programming, Org, Markdown, and prose editing.
GPLv3