Omahub
← All plugins
T

Display Scanlines

by Ty Richards

Display controls with integer-locked, resolution-aware CRT scanline presets

Security review

No obvious issues detected

Deterministic scan — not a security guarantee

None
Risk level
None
Analyzed commit
858e64e
Scanned
1 month ago

No potentially dangerous behavior detected in the analyzed commit.

Automated analysis only — not a security guarantee.

AI advisory review

No obvious issues detected

Language-model assessment · ~deepseek/deepseek-v4-flash-latest — advisory only

Low
AI risk level
Low
Recommendation
install
Model
~deepseek/deepseek-v4-flash-latest
Analyzed commit
858e64e
Reviewed
1 month ago

The plugin is a display shader and control panel for Omarchy/Hyprland. It modifies Hyprland's screen shader and rounding settings via hyprctl eval, writes state files under ~/.local/state, and includes a QML panel. The code is transparent, uses safe inputs, and performs no privileged operations or external downloads.

  • Uses hyprctl eval to execute Lua code, but inputs are sanitized and derived from local files.
  • Writes to user state directories, which is expected for a plugin.
  • No evidence of obfuscation, credential theft, or destructive actions.
How this check works

This review combines the deterministic scan (the rule-based results above) with an independent look at the plugin's code by a language model. The model reads a trimmed sample of the repository's files, the manifest, and the README, then gives a plain-language risk level and a recommendation: install (no notable danger), review (look closer first), or avoid (clearly dangerous).

It runs on the same analyzed commit as the deterministic scan and is strictly advisory — it is not a security guarantee and never blocks a plugin by itself. A human moderator still reviews plugins before they are listed.

AI advisory only — automated analysis, not a security guarantee.

Install
$ omarchy plugin add https://github.com/TyRichards/omarchy-display-scanlines --enable
Appearance #Hyprland #bar #quickshell

Display Scanlines

Omarchy’s Display panel with resolution-aware CRT scanline presets. It preserves the stock brightness, text-size, display-scale, and monitor controls, then adds a 󰯉 CRT Scanlines power toggle with Light and Heavy presets.

<p align="center"> <img src="preview.png" alt="Display Scanlines main preview" width="100%"> </p>

Screenshot disclaimer: The scanlines look better in person than they do in compressed or browser-scaled screenshots, which can soften the effect and introduce moiré.

The root preview is the original lossless 1280×913 PNG, displayed responsively at full README width with no destructive resampling or JPEG conversion.

Presets

Preset Character
Light Subtle, all-day scanlines with no warmth
Heavy Darker gaps, warmer phosphors, deeper vignette, and stronger saturation

The power toggle unloads the shader without changing the selected preset. Light or Heavy can also be selected while power is off, and that choice is restored the next time the effect is enabled. When switching off, the plugin first presents a neutral passthrough shader for multiple full frames, then unloads the visually identical pipeline. Each handoff also triggers Hyprland’s full monitor-damage path—not only its screen-shader refresh—so wallpaper and layer surfaces such as the top bar cannot alternate stale buffers. Once Off, no shader remains loaded.

Both presets use the same scanline pitch and CONTRAST 0.34. They differ in beam profile, color, warmth, vignette, saturation, and the strength of 4K phosphor texture—not line size or midtone contrast.

Everything from Omarchy’s stock Display panel continues to work:

  • Display brightness
  • Shell and GTK text size
  • Display scale presets
  • Multi-monitor enable/disable controls
  • Scroll-wheel brightness adjustment and OSD

Additional previews

<p align="center"> <img src="screenshots/terminal.png" alt="CRT scanlines across a terminal music visualizer" width="100%"> </p> <p align="center"> <img src="screenshots/desktop.png" alt="Display Scanlines panel across the full Omarchy desktop" width="100%"> </p>

Why the scanlines stay even

The effect is calculated from physical output pixels rather than logical coordinates:

  1. textureSize(tex, 0) returns the physical framebuffer size for each output. A 2256×1504 display at 1.6× scale reports 2256×1504—not 1410×940.
  2. gl_FragCoord maps 1:1 to physical pixels.
  3. Beam phase uses integer modulo: int(gl_FragCoord.y) % pitch.

Typical sine/UV scanlines can accumulate phase error and produce drifting line thickness. Here, every scanline cell is generated by identical integer arithmetic. Only symmetric 2px and 4px cells are used—never the asymmetric 3px pattern that made 4K alternate between one-row and two-row bands. Every cell is physically 50% lit / 50% dark: one row per half at 2px, or two rows per half at 4px.

Output height Pitch Scanlines
720 4px 180
900 4px 225
1000 4px 250
1080 2px 540
1504 2px 752
1600 2px 800
1920 (native portrait) 4px 480
2160 (4K) 4px 540
2880 4px 720
4320 (8K) 4px 1080

The shader resolves output dimensions independently on each render pass, so different monitors, scales, hotplug events, and mode changes require no shader regeneration. A native 1000×1920 portrait framebuffer receives the visible 4px high-density Light profile. Hyprland applies screen shaders before output rotation, however, so a landscape framebuffer rotated into portrait also rotates the scanline axis; Hyprland currently exposes no monitor-transform uniform to custom screen shaders.

Image-quality details

  • Linear-light processing: samples use the exact piecewise sRGB transfer before beam and color calculations.
  • Color-stable scanlines: no per-column RGB aperture mask, avoiding colored box ghosts on LCD subpixels, fractionally scaled UIs, browsers, and screenshots.
  • 4K phosphor notches: 4K-class landscape outputs add a subtle, monochrome three-column ramp for incremental left-to-right texture without altering source hue.
  • Damage-safe 4K softness: a gentle phosphor response softens antialiased text edges without spatial blur, glow, or neighboring texture reads.
  • Damage-safe sampling: every output pixel reads only its matching source pixel, so moving UI cannot expose stale/background-colored boxes across Hyprland damage rectangles.
  • Resolution-independent 50/50 bands: every 2px cell is one lit plus one dark row; every 4px cell is two lit plus two translucent-dark rows.
  • Analytic brightness compensation: average scanline-beam energy is normalized instead of using an arbitrary brightness fudge factor.
  • Brightness-dependent beam width: highlights use a wider beam, like a real CRT.
  • Clean zero-cost Off: power transitions repaint the complete output through a neutral handoff before genuinely unloading the shader, preventing layer/background strobe without leaving an identity shader running.
  • Static shader: no time uniform, flicker, or rolling animation, so Hyprland damage tracking and idle efficiency remain effective.

Requirements

  • Omarchy Quattro
  • Hyprland with Lua configuration and decoration.screen_shader support
  • No external packages, elevated privileges, or install hooks

The plugin invokes only Omarchy/Hyprland commands already present on an Omarchy system. Power and preset state are stored beneath ~/.local/state/omarchy/; the plugin does not overwrite user configuration.

Install

Install and enable the plugin:

omarchy plugin add https://github.com/TyRichards/omarchy-display-scanlines.git --enable

Disable the stock Display widget to avoid showing two display panels:

omarchy plugin disable omarchy.monitor

If needed, place Display Scanlines in the right bar section:

omarchy bar move io.github.tyrichards.display-scanlines --section right

Use

Use the switch at the bottom of the Display panel to turn scanlines on or off. Click Light or Heavy to select the preset independently; the selected button remains active while power is off.

The bundled CLI can also control power and preset separately:

~/.config/omarchy/plugins/io.github.tyrichards.display-scanlines/bin/display-scanlines on
~/.config/omarchy/plugins/io.github.tyrichards.display-scanlines/bin/display-scanlines off
~/.config/omarchy/plugins/io.github.tyrichards.display-scanlines/bin/display-scanlines toggle
~/.config/omarchy/plugins/io.github.tyrichards.display-scanlines/bin/display-scanlines light
~/.config/omarchy/plugins/io.github.tyrichards.display-scanlines/bin/display-scanlines heavy
~/.config/omarchy/plugins/io.github.tyrichards.display-scanlines/bin/display-scanlines status

Shell IPC is available as well:

omarchy-shell display-scanlines crt toggle
omarchy-shell display-scanlines crt light
omarchy-shell display-scanlines crt heavy
omarchy-shell display-scanlines state

Optional keybinding

SUPER + CTRL + MINUS makes a fitting power shortcut—the minus key is the scanline. It toggles the effect without changing the saved Light/Heavy preset:

-- This chord is declared upstream as code:20, so unbind that exact form.
hl.unbind("SUPER + CTRL + code:20")
o.bind("SUPER + CTRL + code:20", "Toggle CRT scanlines", "omarchy-shell -q display-scanlines crt toggle")

This replaces Omarchy’s default Expand window left a lot binding.

Persistence and generated files

The selected Light/Heavy preset is saved independently of power in:

~/.local/state/omarchy/display-scanlines/preset

While enabled, login and hyprctl reload persistence is provided by:

~/.local/state/omarchy/toggles/hypr/zz-display-scanlines.lua

Turning power off removes that toggle module but keeps the preset file. Generated shaders also live beneath ~/.local/state/omarchy/display-scanlines/.

The two presets share one source body, shaders/crt.body.glsl. The CLI prepends CRT_LEVEL and rebuilds the selected generated shader whenever needed. Do not edit generated files.

Remove

Turn the effect off, remove the plugin, and restore Omarchy’s stock Display widget:

~/.config/omarchy/plugins/io.github.tyrichards.display-scanlines/bin/display-scanlines off
omarchy plugin remove io.github.tyrichards.display-scanlines --yes
omarchy plugin enable omarchy.monitor right

Validate

omarchy plugin validate ~/.config/omarchy/plugins/io.github.tyrichards.display-scanlines
qmllint -I "$OMARCHY_PATH/shell" \
  ~/.config/omarchy/plugins/io.github.tyrichards.display-scanlines/Panel.qml

Implementation notes

  • GLSL preprocessor conditions use integer USE_* flags. Floating-point literals in #if are invalid and can silently select the wrong branch on some drivers.
  • scanlinePitch() exists in both GLSL and Model.js; the shader is authoritative, while JavaScript supplies the panel readout.
  • The Heavy preset’s Framework-sized 2px gaps are controlled primarily by BEAM_MAX. In every 4px profile, WIDE_GAP controls how much source light remains visible through the equal-width dark half.
  • FOUR_K_SOFTNESS and FOUR_K_NOTCH apply only when the physical framebuffer is at least 3000×1800, leaving the 2256×1504 Framework render path untouched.

License

MIT. The Display panel and model were derived from Omarchy’s MIT-licensed native Display widget. See LICENSE.