This project demonstrates the power of Claude Code Skills — a way to teach Claude specialized knowledge that it can apply automatically. The entire festive shader application you see here was built by Claude using a custom skill for Flutter fragment shader development.
The workflow was simple:
- Create a skill that teaches Claude about Flutter shaders
- Ask Claude to "create a festive shader showcase"
- Claude automatically applied the skill knowledge to build 7 complete GLSL shaders, Dart integration code, and a polished app
No manual shader coding required. Just a skill + a prompt = a complete GPU-powered application.
Seven beautiful shader scenes that auto-play as a journey from Christmas to New Year:
| Scene | Effect | Interaction |
|---|---|---|
| Snowfall | Peaceful winter night with falling snow | Mouse creates warm glow, shake adds wind |
| Aurora | Northern lights dancing across the sky | Hold space (hiss) intensifies the aurora |
| Matrix | Developer-style terminal rain | Mouse reveals "HAPPY 2026" message |
| Countdown | Epic 10-9-8... countdown with energy rings | Mouse creates ripples, hiss adds glow |
| Fireworks | Explosive celebration! | Tap anywhere to launch fireworks |
| Celebration | 2026 party with confetti & disco | Shake adds bass effect |
| Finale | Cyberpunk "Happy Coding 2026" neon text | Shake adds glitch effects |
Click to expand the full skill prompt
This is the comprehensive skill document that was created to teach Claude about Flutter shader development:
This is a comprehensive, agent-oriented "playbook" for building, integrating, debugging, and shipping Flutter fragment shaders using Flutter's FragmentProgram / FragmentShader APIs.
Primary reference: Flutter's official "Writing and using fragment shaders" guide.
Supported shader type
- Flutter supports fragment shaders (pixel shaders). It does not support vertex shaders in the SDK's shader pipeline.
Core runtime objects
FragmentProgram: a compiled shader asset that can create shader instances.FragmentShader: an instance with a particular set of uniforms (float values + samplers) that you bind intoPaint.shader, or sometimes intoImageFilter.shader(Impeller-only).
Backends (why behavior differs)
- Flutter runs with Skia or Impeller backends depending on platform/config. Both support custom shaders, but some APIs differ (notably
ImageFilter.shaderis Impeller-only).
The agent should know / be able to derive:
- GLSL basics: uniform, vec2/3/4, sampler2D, texture(), coordinate normalization, time-driven animation.
- Flutter rendering primitives: CustomPainter, Canvas, Paint.shader, ShaderMask, ImageFiltered/BackdropFilter patterns.
- Performance concerns: shader compilation jank + mitigation strategies.
A. Put shader files under an assets folder
Typical layout:
/shaders
my_effect.frag
/lib
shaders/
widgets/
B. Declare shaders in pubspec.yaml
Flutter compiles declared .frag files and bundles them like assets.
flutter:
shaders:
- shaders/my_effect.fragC. Hot reload behavior
- In debug, editing a shader triggers recompilation and updates on hot reload/hot restart.
Start with the canonical header + include:
#version 460 core
#include <flutter/runtime_effect.glsl>
out vec4 fragColor;Coordinate access (important)
Use FlutterFragCoord() (not gl_FragCoord) for the current fragment position.
Common limitations (don't fight them)
Flutter's shader pipeline restricts features; key ones include:
- Only
sampler2Dis supported (no samplerCube, etc.) - Only the two-argument
texture(sampler, uv)form - No extra varying inputs
- No UBO/SSBO
- Unsigned ints and booleans not supported
Impeller + OpenGLES texture Y-flip (when sampling engine textures)
If you use ImageFilter.shader / BackdropFilter input textures, you may need to un-flip UVs on OpenGLES:
vec2 uv = FlutterFragCoord().xy / u_size;
#ifdef IMPELLER_TARGET_OPENGLES
uv.y = 1.0 - uv.y;
#endifRule 1: float uniforms are indexed in declaration order
float / vec2 / vec3 / vec4 are set via setFloat(index, value) in the order declared.
Rule 2: samplers use a separate index space
sampler2D uniforms are set via setImageSampler(samplerIndex, image) and do not consume float indices.
Example (pay attention to indexing):
uniform float uScale;
uniform sampler2D uTexture;
uniform vec2 uMagnitude;
uniform vec4 uColor;shader.setFloat(0, 23); // uScale
shader.setFloat(1, 114); // uMagnitude.x
shader.setFloat(2, 83); // uMagnitude.y
shader.setFloat(3, r); shader.setFloat(4, g); shader.setFloat(5, b); shader.setFloat(6, a);
shader.setImageSampler(0, image); // uTexture (samplers start at 0)A. Load a program from an asset
final program = await FragmentProgram.fromAsset('shaders/my_effect.frag');B. Create a shader instance and bind uniforms
final shader = program.fragmentShader();
shader.setFloat(0, timeSeconds);
shader.setFloat(1, width);
shader.setFloat(2, height);Then draw with it:
canvas.drawRect(rect, Paint()..shader = shader);C. Apply as an ImageFilter (Impeller-only)
Flutter provides ImageFilter.shader(shader) for ImageFiltered / BackdropFilter, but it is only supported by Impeller.
Step 1 — Translate effect into a shader spec
Write a mini-spec like:
- Inputs (uniforms): u_time, u_size, parameters (strength, color, frequency, seed)
- Whether it samples a texture (sampler2D) or is purely procedural
- Where it's applied: full-screen rect, clipped region, mask, backdrop, etc.
Step 2 — Create a stable uniform contract
Use a consistent uniform naming & ordering convention per shader:
uniform vec2 u_size;(or pass as two floats)uniform float u_time;- Effect params…
Then mirror that order in Dart with constants/enums for indices.
Step 3 — Implement integration wrapper (reusable widget)
Typical wrapper choices:
- CustomPainter for drawing into a Canvas
- ShaderMask for masking child content
- BackdropFilter / ImageFiltered for post-processing (Impeller-only)
Step 4 — Performance hardening
- Reuse FragmentShader where possible instead of creating a new instance each frame
- On Skia, shader compilation can be expensive at runtime; precache FragmentProgram before an animation starts
- If you hit "first animation jank" on Skia, apply SkSL warm-up capture & bundle workflow
- For classic (non-custom) jank mitigation, ShaderWarmUp exists to pre-trigger shader compilation
- Consider Impeller where available: it precompiles engine shaders at build-time to reduce runtime compilation stalls
Step 5 — Cross-platform validation
- Test on at least: Android (OpenGLES/Vulkan differences), iOS (Metal), and Web if you target it.
- Web renderer choice matters; Flutter Web has multiple renderers (e.g., CanvasKit vs skwasm) and build modes.
A. "Black screen / nothing renders"
Common causes:
fragColornever written- Wrong coordinate normalization (e.g., using raw FlutterFragCoord without dividing by size)
- Uniform indices don't match declaration order (especially around samplers)
B. Uniform layout auditing (deep debugging)
Flutter's shader pipeline can output reflection data; use impellerc + flatc to inspect uniforms/offsets.
C. Backend-specific runtime error
- If using
ImageFilter.shaderon non-Impeller backend, Flutter warns it throws.
Template A — Pure procedural full-rect shader
shaders/wave.frag:
#version 460 core
#include <flutter/runtime_effect.glsl>
out vec4 fragColor;
uniform vec2 u_size;
uniform float u_time;
uniform float u_amp;
uniform float u_freq;
void main() {
vec2 p = FlutterFragCoord().xy / u_size;
float y = sin((p.x * u_freq + u_time) * 6.28318) * u_amp;
float v = smoothstep(0.02, 0.0, abs(p.y - (0.5 + y)));
fragColor = vec4(vec3(v), 1.0);
}Template B — Post-processing via BackdropFilter (Impeller-only)
Use the pattern: BackdropFilter(filter: ImageFilter.shader(shader)) and clip to constrain area.
Use these as the agent's canonical sources:
- Flutter official guide: "Writing and using fragment shaders"
- Dart API: FragmentProgram docs
- Flutter performance: Impeller overview
- SkSL warm-up workflow (Skia backend)
- Dart API: ShaderWarmUp
- Web renderer overview
- Utility package: flutter_shaders
This project includes Claude Code integrations to help you work with Flutter shaders:
Quick command for shader tasks:
/shader create a plasma effect with rainbow colors
/shader debug why my shader shows black
/shader explain how uniform indexing works
/shader optimize my fireworks shader for mobileA specialized agent that automatically activates when you're working with shaders. It knows:
- How to create new shaders from effect descriptions
- How to debug common issues (black screen, wrong colors, texture problems)
- Flutter's uniform indexing rules (the #1 source of shader bugs!)
- Cross-platform considerations (Skia vs Impeller, OpenGLES Y-flip)
The core knowledge base in .claude/skills/flutter-shader/:
SKILL.md— Complete development guideTEMPLATES.md— 6 copy-paste shader patternsREFERENCE.md— API docs & troubleshooting
- Mouse/Touch: Move for position-based effects
- Tap/Click: Trigger scene-specific events
- Hold Space: "Hiss" effect (simulates audio input)
- Arrow Keys: "Shake" effect (simulates accelerometer)
- Scene Chips: Jump to any scene directly
- Auto/Manual: Toggle auto-progression
flutter runWorks best on:
- Desktop (macOS, Windows, Linux)
- iOS/Android (Impeller backend recommended)
- Web (CanvasKit renderer)
.claude/
skills/flutter-shader/ # The skill that taught Claude shaders
SKILL.md # Core knowledge
TEMPLATES.md # Starter patterns
REFERENCE.md # API & troubleshooting
commands/
shader.md # /shader command
agents/
shader-assistant.md # Proactive shader agent
shaders/
snowfall.frag # Procedural snowflakes + aurora hint
aurora.frag # Northern lights with mountains
matrix.frag # Code rain with hidden message
countdown.frag # 7-segment countdown display
fireworks.frag # Particle explosions + city silhouette
celebration.frag # 2026 text + confetti + disco
cyberpunk_ending.frag # Neon "Happy Coding 2026" finale
lib/
main.dart # App entry + splash screen
shaders/
shader_cache.dart # Preloading & caching
widgets/
interaction_detector.dart # Mouse, tap, shake, hiss handling
scenes/
scene_painter.dart # CustomPainter implementations
festive_journey.dart # Scene orchestration
- Procedural noise (FBM, gradient noise) for aurora & backgrounds
- Particle systems for snow, fireworks, confetti
- SDF rendering for countdown digits
- Post-processing patterns for matrix effect
- Color space conversions (HSV) for rainbow effects
FragmentProgram.fromAsset()with warm-up cachingFragmentShaderuniform binding with correct index mappingCustomPainterfor efficient shader rendering- Smooth scene transitions with
AnimationController
All shaders follow a consistent pattern:
uniform vec2 u_size; // indices 0-1
uniform float u_time; // index 2
// ...scene-specific paramsThis project demonstrates:
- How to write Flutter-compatible GLSL (use
FlutterFragCoord(), notgl_FragCoord) - Proper uniform indexing (vec2 = 2 floats, samplers are separate)
- Performance optimization via shader caching
- Interactive shader parameters driven by user input
- How to create Claude Code skills for specialized domains
- Clone this repo
- Run
flutter run - Use
/shader create [your effect]to add new shaders - Check
.claude/skills/flutter-shader/to see how the skill works - Adapt the skill pattern for your own specialized domains!
Built by Moinsen Development using Claude Code.
Creative AI-powered development. Crazy ideas. Fast delivery. Decades of experience.
At Moinsen Development, we specialize in agentic AI coding — turning ambitious visions into reality at lightning speed. We combine deep software engineering expertise with cutting-edge AI tools to deliver what others think is impossible.
This project showcases how Claude Code skills can encode specialized knowledge and enable Claude to build complete applications from high-level descriptions. The entire festive shader journey — from concept to polished app — was created in a single Claude Code session.
Made with GLSL, Flutter & Claude Code for the developer community.
Happy Coding 2026! 🚀