A Neovim plugin for live Slang shader debugging — preview what any line of your fragment shader outputs, right in the editor.
- Live shader previews — move your cursor to any Slang line and see what it outputs rendered on a quad
- Reflection-driven — automatically detects textures, buffers, samplers, and uniform bindings from your shader
- Interactive inputs — click or press Enter on any input to bind custom images, textures, or JSON data
- Dual backend — render previews through Vulkan or headless OpenGL/EGL
- Non-blocking — everything runs in the background, so your editor never stutters
{
"NickTsaizer/shaderdebug.nvim",
ft = { "slang" },
dependencies = { "3rd/image.nvim" },
}- Neovim 0.10+
- image.nvim — for displaying rendered previews inline
- slangc — Slang compiler (
~/VulkanSDK/.../bin/slangc) - glslangValidator — for SPIR-V compilation
- Vulkan — required when
api = "vulkan"orapi = "auto" - EGL + OpenGL — required when
api = "opengl"(headless/offscreen path) - libepoxy — OpenGL function loading for the headless EGL path
- ffmpeg — for non-PNG image conversion
- libpng — PNG reading/writing
" Open a Slang shader file
:e myshader.slang
" Preview the current line
:ShaderDebugPreview
" Enable auto-preview on cursor move
:ShaderDebugToggleAutoChoose a backend globally in setup():
require("shaderdebug").setup({
api = "vulkan", -- "vulkan", "opengl", or "auto" (currently falls back to Vulkan)
})| Command | Description |
|---|---|
:ShaderDebugPreview |
Render preview for the current line |
:ShaderDebugToggleAuto |
Toggle live preview on cursor move |
:ShaderDebugInputs |
Show reflected inputs in the message area |
:ShaderDebugSetImage <name> <path> |
Bind a single image to an input |
:ShaderDebugSetImages <name> <paths> |
Bind multiple images (comma-separated) |
:ShaderDebugSetData <name> <json> |
Bind JSON data to a buffer input |
:ShaderDebugClearInput <name> |
Clear an input override |
:ShaderDebugClear |
Close the preview and disable auto-preview |
The preview split shows:
- Header —
<entry> > <expression> - API — backend being used (e.g.
vulkan) - Inputs — each reflected resource with current binding
Move the cursor onto an input line and press:
- Enter — edit that input (prompt for image path, JSON data, etc.)
- x — clear the input override
- r — refresh the preview
Pass options to setup():
require("shaderdebug").setup({
api = "vulkan", -- "vulkan", "opengl", or "auto"
auto_preview = false, -- start with auto-preview disabled
debounce_ms = 180, -- delay before rendering
image_size = 512, -- preview image resolution
slangc = "slangc", -- path to slangc
glslang_validator = "glslangValidator",
})api = "vulkan"compiles Slang to SPIR-V and uses the Vulkan offscreen rendererapi = "opengl"compiles Slang to GLSL and uses a headless EGL/OpenGL rendererapi = "auto"currently resolves to Vulkan
Texture2D<float4> albedo;
SamplerState albedoSampler;
struct FragmentInput
{
float2 uv: TEXCOORD0;
};
[[shader("fragment")]]
float4 fragment(FragmentInput input) : SV_Target0
{
return albedo.Sample(albedoSampler, input.uv);
}Bind the texture from Neovim:
:ShaderDebugSetImage albedo /path/to/image.png
:ShaderDebugPreview- Only fragment shaders are supported
- Works best with simple expressions (assignments, returns)
- OpenGL currently treats sampled textures as combined
sampler2Duniforms internally
Built with Slang, Vulkan, EGL/OpenGL, and image.nvim.
