anywidget-glsl
Write GPU-accelerated GLSL shaders in marimo notebooks with a Shadertoy-like API built on AnyWidget.
Features
- 🎨 Shadertoy-compatible API - Familiar
mainImage()signature with built-in uniforms - 🔗 String-free wiring - Connect shader passes using class attributes instead of string names
- 🎬 Multi-pass rendering - Compose complex effects with feedback buffers
- 🎮 Interactive inputs - Built-in support for mouse, keyboard, webcam, and time
- 🔧 Full Pipeline API - Explicit vertex+fragment shader control for advanced use cases
- 📊 Type-safe uniforms - Python↔GLSL binding with numpy arrays and textures
- 🐛 Clear error messages - Source wrapped with
#linedirectives for precise GLSL debugging
Installation
uv add anywidget-glsl
Development Installation
git clone https://github.com/dmadisetti/anywidget-glsl.git
cd anywidget-glsl
pip install -e ".[dev]"
Quick Start
Simple Animated Shader
from anywidget_glsl import ToyGLSL
class Rainbow(ToyGLSL):
time = True # Enable iTime uniform
_glsl = r"""
void mainImage(out vec4 fragColor, in vec2 fragCoord) {
vec2 uv = fragCoord / iResolution.xy;
vec3 color = 0.5 + 0.5 * cos(iTime + uv.xyx + vec3(0, 2, 4));
fragColor = vec4(color, 1.0);
}
"""
Rainbow() # Display in notebook
Interactive with Mouse
class Interactive(ToyGLSL):
time = True
mouse = True # Enable iMouse uniform
_glsl = r"""
void mainImage(out vec4 fragColor, in vec2 fragCoord) {
vec2 uv = fragCoord / iResolution.xy;
vec2 mouse = iMouse.xy / iResolution.xy;
float dist = length(uv - mouse);
vec3 color = vec3(1.0 - smoothstep(0.0, 0.3, dist));
fragColor = vec4(color, 1.0);
}
"""
Interactive()
ToyGLSL API (Image Shaders)
The ToyGLSL API is inspired by Shadertoy and focuses on fragment shaders with automatic uniform management.
Built-in Uniforms
Enable uniforms by setting class attributes:
class MyShader(ToyGLSL):
time = True # → uniform float iTime
mouse = True # → uniform vec4 iMouse (xy = pos, zw = click state)
webcam = True # → uniform sampler2D iWebcam
keyboard = True # → uniform sampler2D iKeyboard (256×1 texture)
Always available:
uniform vec3 iResolution- Canvas size (width, height, 1.0)uniform float iTimeDelta- Time since last frameuniform float iFrame- Frame counteruniform float iFrameRate- Frames per seconduniform vec4 iDate- (year, month, day, time in seconds)
Multi-Pass Composition
Create complex effects by chaining shader passes:
class BlurPass(ToyGLSL):
_glsl = r"""
void mainImage(out vec4 fragColor, in vec2 fragCoord) {
vec2 uv = fragCoord / iResolution.xy;
vec4 sum = vec4(0.0);
for(int i = -2; i <= 2; i++) {
for(int j = -2; j <= 2; j++) {
vec2 offset = vec2(i, j) / iResolution.xy;
sum += texture(iPrevPass, uv + offset);
}
}
fragColor = sum / 25.0;
}
"""
class Final(ToyGLSL):
time = True
_buffers = (BlurPass,) # BlurPass runs first
_glsl = r"""
void mainImage(out vec4 fragColor, in vec2 fragCoord) {
vec2 uv = fragCoord / iResolution.xy;
vec3 blurred = texture(iPrevPass, uv).rgb;
vec3 color = blurred * (0.5 + 0.5 * sin(iTime));
fragColor = vec4(color, 1.0);
}
"""
Final()
Feedback Buffers
Use backbuffer=True to access the previous frame for trail effects:
class Trail(ToyGLSL):
time = True
mouse = True
backbuffer = True # → uniform sampler2D iBackbuffer
_glsl = r"""
void mainImage(out vec4 fragColor, in vec2 fragCoord) {
vec2 uv = fragCoord / iResolution.xy;
vec2 mouse = iMouse.xy / iResolution.xy;
// Read previous frame
vec4 prev = texture(iBackbuffer, uv);
// Add new content near mouse
float dist = length(uv - mouse);
vec3 add = vec3(1.0 - smoothstep(0.0, 0.1, dist));
// Fade and accumulate
fragColor = vec4(prev.rgb * 0.95 + add, 1.0);
}
"""
Trail()
String-Free Connectors
Wire shader passes without string names using class attribute references:
class PassA(ToyGLSL):
time = True
_glsl = r"""
void mainImage(out vec4 fragColor, in vec2 fragCoord) {
vec2 uv = fragCoord / iResolution.xy;
fragColor = vec4(uv, sin(iTime) * 0.5 + 0.5, 1.0);
}
"""
class PassB(ToyGLSL):
time = True
source_a = PassA.frag # Reference PassA's output (no strings!)
_glsl = r"""
void mainImage(out vec4 fragColor, in vec2 fragCoord) {
vec2 uv = fragCoord / iResolution.xy;
vec3 a = texture(source_a, uv).rgb;
fragColor = vec4(1.0 - a, 1.0); // Invert colors
}
"""
PassB() # PassA runs automatically as a dependency
Pipeline API (Vertex + Fragment)
For advanced use cases requiring full control over the vertex and fragment pipeline:
from anywidget_glsl import uniform
from anywidget_glsl.pipeline import (
VertexProgram, FragmentProgram, In, Out, Color, PipelineFactory
)
class MyVertex(VertexProgram):
# Declare vertex attributes
position = In[uniform.Vec2](loc=0).tag(sync=True)
color = In[uniform.Vec3](loc=1).tag(sync=True)
# Declare varyings
v_color = Out[uniform.Vec3]()
gl_Position = Out[uniform.Vec4]()
_glsl = """
void main() {
v_color = color;
gl_Position = vec4(position, 0.0, 1.0);
}
"""
class MyFragment(FragmentProgram):
# Receive varyings from vertex shader
v_color = MyVertex.v_color
# Declare color output
color0 = Color[uniform.Vec4](loc=0)
_glsl = """
void main() {
color0 = vec4(v_color, 1.0);
}
"""
# Create widget from pipeline
MyPipeline = PipelineFactory(MyFragment.color0)
# Use in notebook
import numpy as np
positions = np.array([[-0.5, -0.5], [0.5, -0.5], [0.0, 0.5]], dtype=np.float32)
colors = np.array([[1, 0, 0], [0, 1, 0], [0, 0, 1]], dtype=np.float32)
widget = MyPipeline(position=positions, color=colors)
widget
Examples
Check out the examples/ directory for more demos:
basic_shader.py- Simple animated shadersinteractive_uniforms.py- Mouse and time interactionsmultipass_feedback.py- Multi-pass rendering with backbufferspipeline_demo.py- Vertex+fragment pipeline examplesfilters_demo.py- Built-in filter effects
API Reference
ToyGLSL Class Attributes
| Attribute | Type | Description |
|---|---|---|
time |
bool |
Enable iTime uniform |
mouse |
bool |
Enable iMouse uniform (vec4) |
webcam |
bool |
Enable iWebcam texture |
keyboard |
bool |
Enable iKeyboard texture (256×1) |
backbuffer |
bool |
Enable iBackbuffer (previous frame) |
_glsl |
str |
Fragment shader source with mainImage() |
_buffers |
tuple |
Tuple of ToyGLSL classes for multi-pass |
Uniform Types
From anywidget_glsl.uniform:
Vec2,Vec3,Vec4- Vector uniformsMat2,Mat3,Mat4- Matrix uniformsFloat,Int- Scalar uniformsTexture2D- Image textures
All uniform types are traitlets and must be tagged with .tag(sync=True) for Python↔JavaScript synchronization.
Built-in Filters
from anywidget_glsl.filters import Blur, Trail
# Apply Gaussian blur
blurred = Blur(source=MyShader.frag, radius=5.0)
# Apply trail effect
trails = Trail(source=MyShader.frag, decay=0.95)
Requirements
- Python ≥ 3.13
- anywidget ≥ 0.9.0
- numpy ≥ 2.3.4
- marimo ≥ 0.17.2 (for notebook environments)
Browser Compatibility
Requires WebGL 2.0 support. Compatible with:
- Chrome/Edge 56+
- Firefox 51+
- Safari 15+
Contributing
Contributions are welcome! Please see CONTRIBUTING.md for development guidelines.
License
Apache License 2.0 - see LICENSE for details.
Acknowledgments
Built on AnyWidget by Trevor Manz.
Inspired by Shadertoy by Iñigo Quilez and Pol Jeremias.
Metadata
Release files for anywidget-glsl 1.0.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| anywidget_glsl-1.0.0.tar.gz | 33.0 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| anywidget_glsl-1.0.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 79.3 kB
Release files / anywidget_glsl-1.0.0.tar.gz
| Download URL | anywidget_glsl-1.0.0.tar.gz |
|---|---|
| Size | 33.0 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
4d6b2e00a1188c9742594af60379de30968dab27908348181cff7cdc21f51aaf
|
|
BLAKE2b-256 checksum How to use checksums |
c0033a4a8788078cf491435d1095d3f85fb89c64cbd1e08c9c4048345bc9d3bb
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.7
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Jan 6, 2026.
Transparency logRelease files / anywidget_glsl-1.0.0-py3-none-any.whl
| Download URL | anywidget_glsl-1.0.0-py3-none-any.whl |
|---|---|
| Size | 46.4 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
8310071d6453e8dc22d4fa6f5ede51bb8e1e7bf531395d81789939322526c7df
|
|
BLAKE2b-256 checksum How to use checksums |
0bda6e35eec1241c9444729d6b36e01a5c24cd01a2dbabfd0d794abd71242c51
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.7
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Jan 6, 2026.
Transparency log