Write GPU-accelerated GLSL shaders in Jupyter notebooks with a Shadertoy-like API
Project description
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.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file anywidget_glsl-1.0.0.tar.gz.
File metadata
- Download URL: anywidget_glsl-1.0.0.tar.gz
- Upload date:
- Size: 33.0 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
4d6b2e00a1188c9742594af60379de30968dab27908348181cff7cdc21f51aaf
|
|
| MD5 |
25f757e590147e6120aaec89ebc9fec6
|
|
| BLAKE2b-256 |
c0033a4a8788078cf491435d1095d3f85fb89c64cbd1e08c9c4048345bc9d3bb
|
Provenance
The following attestation bundles were made for anywidget_glsl-1.0.0.tar.gz:
Publisher:
publish.yml on dmadisetti/anywidget-glsl
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
anywidget_glsl-1.0.0.tar.gz -
Subject digest:
4d6b2e00a1188c9742594af60379de30968dab27908348181cff7cdc21f51aaf - Sigstore transparency entry: 798536949
- Sigstore integration time:
-
Permalink:
dmadisetti/anywidget-glsl@5d66661bec24163865f2f4dcb368c386b6da651b -
Branch / Tag:
refs/tags/v1.0.0 - Owner: https://github.com/dmadisetti
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@5d66661bec24163865f2f4dcb368c386b6da651b -
Trigger Event:
release
-
Statement type:
File details
Details for the file anywidget_glsl-1.0.0-py3-none-any.whl.
File metadata
- Download URL: anywidget_glsl-1.0.0-py3-none-any.whl
- Upload date:
- Size: 46.4 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
8310071d6453e8dc22d4fa6f5ede51bb8e1e7bf531395d81789939322526c7df
|
|
| MD5 |
5a211b55f5e4cd6a0b1a2454e8ebd1c1
|
|
| BLAKE2b-256 |
0bda6e35eec1241c9444729d6b36e01a5c24cd01a2dbabfd0d794abd71242c51
|
Provenance
The following attestation bundles were made for anywidget_glsl-1.0.0-py3-none-any.whl:
Publisher:
publish.yml on dmadisetti/anywidget-glsl
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
anywidget_glsl-1.0.0-py3-none-any.whl -
Subject digest:
8310071d6453e8dc22d4fa6f5ede51bb8e1e7bf531395d81789939322526c7df - Sigstore transparency entry: 798536969
- Sigstore integration time:
-
Permalink:
dmadisetti/anywidget-glsl@5d66661bec24163865f2f4dcb368c386b6da651b -
Branch / Tag:
refs/tags/v1.0.0 - Owner: https://github.com/dmadisetti
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@5d66661bec24163865f2f4dcb368c386b6da651b -
Trigger Event:
release
-
Statement type: