A minimal desktop shader editor for writing and running WebGL2 fragment shaders.
Shady is built around a quiet Monaco editor, a fullscreen preview, and Shadertoy-style uniforms/channels. It supports shader buffers, image and audio inputs, and live microphone audio-reactive textures.
- Frameless Electron app with a minimal dark interface
- Monaco editor with GLSL syntax highlighting
- GLSL autocomplete for Shady uniforms and built-in functions
- WebGL2 fragment shader preview
- Multi-pass shader buffers
- Image, audio file, and microphone input channels
- Project save/load support
- Tokyo Night-inspired editor theme with a violet accent
- Global channels/buffers (main difference with shadertoy)
- Node.js
- npm
npm install
npm run devnpm run buildTo create a packaged app:
npm run packagePlatform-specific packages:
npm run package:mac
npm run package:win
npm run package:linuxRelease artifacts are written to dist/.
- macOS: DMG
- Windows: ZIP, x64
- Linux: tar.gz, x64
Cmd+Enter/Ctrl+Enter: run the shader previewSpace: pause/resume the shader previewEsc: return to the editorCmd+Shift+F/Ctrl+Shift+F: toggle statsCmd+0/Ctrl+0: projectCmd+1-5/Ctrl+1-5: switch shader tabs
Shaders are WebGL2 fragment shaders and should start with:
#version 300 esCommon uniforms include:
uniform vec2 iResolution;
uniform float iTime;
uniform float iTimeDelta;
uniform int iFrame;
uniform vec4 iMouse;
uniform vec4 iDate;
uniform float iSampleRate;
uniform vec3 iChannelResolution[4];
uniform float iChannelTime[4];
uniform sampler2D iChannel0;
uniform sampler2D iChannel1;
uniform sampler2D iChannel2;
uniform sampler2D iChannel3;Audio and microphone inputs use a 512 x 2 texture:
- row
0: frequency data - row
1: waveform data
Most Shadertoy shaders (GLSL ES 3.0) run in Shady with a small wrapper.
| Shadertoy | Shady |
|---|---|
void mainImage(out vec4 fragColor, in vec2 fragCoord) |
void main() writing to out vec4 fragColor. fragCoord is provided for you (gl_FragCoord.xy), do not declare it. |
| Uniforms are predeclared | Declare them yourself (the starter shader already has the block). iResolution is a vec2, not a vec3. |
| Image tab | main tab |
| Buffer A-D tabs | buffer a - buffer d tabs |
| Per-pass channel inputs | Global channels: iChannel0-3 always read buffers A-D, and any slot can be replaced by an image, audio file, or microphone |
To port a shader:
- Paste the Shadertoy code into the
maintab, below the header and uniform block. - Add a
main()that callsmainImage:
#version 300 es
precision highp float;
uniform vec2 iResolution;
uniform float iTime;
uniform float iTimeDelta;
uniform int iFrame;
uniform vec4 iMouse;
uniform vec4 iDate;
uniform float iSampleRate;
uniform vec3 iChannelResolution[4];
uniform float iChannelTime[4];
uniform sampler2D iChannel0;
uniform sampler2D iChannel1;
uniform sampler2D iChannel2;
uniform sampler2D iChannel3;
out vec4 fragColor;
// --- Shadertoy code, unchanged ---
void mainImage(out vec4 O, in vec2 fragCoord) {
// ...
}
// ---------------------------------
void main() {
mainImage(fragColor, fragCoord);
}- Buffers: paste each into its matching tab (same wrapper). Channels are global, so
iChannel0-3always read buffers A-D. - Press
Cmd+Enter/Ctrl+Enterto run it.
npm run typecheck
npm run buildMIT
