swiftui-graphics
Advanced SwiftUI visuals - Metal shaders (.colorEffect, .layerEffect, .distortionEffect), .visualEffect, Liquid Glass (iOS 26), Canvas, holographic and CRT effects.
git clone --depth 1 https://github.com/Jwuthri/Tracely-ai /tmp/swiftui-graphics && cp -r /tmp/swiftui-graphics/.agents/skills/swiftui-graphics ~/.claude/skills/swiftui-graphicsSKILL.md
# SwiftUI Graphics
> Advanced SwiftUI visuals: Metal shaders, visual effects, Liquid Glass, Canvas.
> Loaded for advanced thesis (shaders, holographic, liquid-glass, distortion).
> Foundation: `../swiftui-motion/SKILL.md` covers the basics.
> Concise rules here. Deep-dives in `references/`.
---
## Decision Tree: Which API?
| Need | API |
|---|---|
| Pixel-level color manipulation | `.colorEffect(ShaderLibrary....)` |
| Pixel position / distortion | `.distortionEffect(ShaderLibrary....)` |
| Full layer with overlay (mix shader + bg) | `.layerEffect(ShaderLibrary....)` |
| View modifier with geometry context | `.visualEffect { content, geometry in }` |
| Custom drawing (paths, gradients) | `Canvas { context, size in }` |
| iOS 26+ glassmorphism | `.glassEffect()` / `GlassEffectContainer` |
| Performance dump | `Canvas` with `.opaque(true)` then export |
> **Default order of escalation:** built-in modifiers -> `.visualEffect` -> `Canvas` -> Metal shader. Reach for shaders only when the effect is per-pixel and animated.
---
## Metal Shaders Intro
SwiftUI binds to Metal Shading Language (MSL) via three modifiers shipped in iOS 17: `.colorEffect`, `.distortionEffect`, `.layerEffect`. You author a `.metal` file in your app target, mark functions with the `[[ stitchable ]]` attribute, and SwiftUI auto-generates the Swift binding via `ShaderLibrary.<functionName>(...)`. One library per app target. Shaders run on the GPU at native resolution; arguments are passed as `.float`, `.float2`, `.color`, `.image` from Swift. iOS 17+ only; for older targets, fall back to gradients, blur, or `Canvas`.
The three slots differ by what data they receive:
- `.colorEffect`: gets `(position, color)`, returns transformed color. No neighbor sampling.
- `.distortionEffect`: gets `(position)`, returns a new sample position. Pixels move, colors do not change.
- `.layerEffect`: gets `(position, SwiftUI::Layer layer)`, returns final color. Can sample anywhere within `maxSampleOffset`. Most expensive.
---
## Recipe: Ripple `.layerEffect`
Touch ripple that displaces nearby pixels along a sine wave.
```swift
struct RippleView: View {
@State var rippleOrigin: CGPoint = .zero
@State var rippleTime: Float = 0
var body: some View {
Image("photo")
.resizable()
.scaledToFit()
.layerEffect(
ShaderLibrary.ripple(
.float2(Float(rippleOrigin.x), Float(rippleOrigin.y)),
.float(rippleTime),
.float(0.05) // amplitude
),
maxSampleOffset: CGSize(width: 50, height: 50)
)
.onTapGesture { location in
rippleOrigin = location
rippleTime = 0
withAnimation(.linear(duration: 1.2)) {
rippleTime = 1.2
}
}
}
}
```
```metal
// Ripple.metal
#include <SwiftUI/SwiftUI_Metal.h>
using namespace metal;
[[ stitchable ]]
half4 ripple(float2 position, SwiftUI::Layer layer,
float2 origin, float time, float amp) {
float distance = length(position - origin);
float wave = sin(distance * 0.05 - time * 8.0) * amp;
float2 dir = normalize(position - origin);
float falloff = 1.0 / max(distance, 1.0);
float2 displaced = position + dir * wave * falloff * 50.0;
return layer.sample(displaced);
}
```
**Why this works:**
- `[[ stitchable ]]` exposes the function to SwiftUI's runtime.
- The first two args (`position`, `SwiftUI::Layer layer`) are injected by SwiftUI for any `.layerEffect`. Your Swift-side args start at index 2.
- `maxSampleOffset` tells SwiftUI how far you may sample beyond the view bounds. Underestimate and you get clipping. Overestimate and you waste GPU.
---
## Recipe: Holographic `.colorEffect`
Oil-slick rainbow shimmer driven by time or scroll offset. Preserves luminance so dark regions stay dark.
```metal
// Holographic.metal
#include <SwiftUI/SwiftUI_Metal.h>
using namespace metal;
[[ stitchable ]]
half4 holographic(float2 position, half4 color, float time) {
float n = position.x * 0.01 + position.y * 0.005 + time * 0.3;
half3 rainbow = half3(
sin(n * 2.0) * 0.5 + 0.5,
sin(n * 2.0 + 2.094) * 0.5 + 0.5,
sin(n * 2.0 + 4.188) * 0.5 + 0.5
);
half luminance = dot(color.rgb, half3(0.299, 0.587, 0.114));
return half4(mix(color.rgb, rainbow * luminance * 2.0, 0.5), color.a);
}
```
```swift
struct HolographicCard: View {
let startTime = Date()
var body: some View {
TimelineView(.animation) { timeline in
let elapsed = Float(timeline.date.timeIntervalSince(startTime))
Image("card")
.resizable()
.scaledToFit()
.colorEffect(
ShaderLibrary.holographic(.float(elapsed))
)
}
}
}
```
> The 2.094 and 4.188 offsets are 2pi/3 and 4pi/3 -- they spread the three sine waves to RGB phases. Keep them.
---
## Recipe: CRT Scanlines
Vintage CRT effect: scanlines, flicker, subtle chromatic aberration.
```metal
// CRT.metal
#include <SwiftUI/SwiftUI_Metal.h>
using namespace metal;
[[ stitchable ]]
half4 crt(float2 position, half4 color, float time) {
float scanline = sin(position.y * 1.5) * 0.04;
float flicker = sin(time * 60.0) * 0.02;
half3 result = color.rgb * (1.0 - scanline - flicker);
// chromatic aberration on R/B channels
return half4(result.r * 1.05, result.g, result.b * 1.05, color.a);
}
```
```swift
.colorEffect(ShaderLibrary.crt(.float(elapsed)))
```
> For real chromatic aberration (shifted R/B sample positions), promote to `.layerEffect`. The version above only tints, which reads as CRT at small scale.
---
## `.visualEffect` (iOS 17+)
Modifier that exposes the view's `GeometryProxy` so you can react to its frame in any coordinate space without `GeometryReader` boilerplate.
```swift
ScrollView {
LazyVStack(spacing:Algorithmic and generative art with Canvas 2D - particles, flow fields, noise, fractals, L-systems.
Cast genjutsu on a UI - creative coding for motion, micro-interactions, and wow-factor. Scans the stack, proposes an interaction thesis, loads the right sub-skills, implements the illusion. Adapts to Web, Android (Compose), Apple (SwiftUI).
Advanced Compose visuals - Material 3 Expressive motion physics, AGSL shaders (Android 13+), Canvas/DrawScope generative, graphicsLayer effects.
Jetpack Compose animation foundations - animate*AsState, AnimatedVisibility, Crossfade, updateTransition, SharedTransitionLayout, gestures.
Compose Multiplatform / KMP patterns - expect/actual composables, platform-specific code, density and font handling cross-target, iOS/Android/Desktop interop.
Zero-dependency animations and visual techniques - scroll-driven, View Transitions, @starting-style, modern CSS.
Design audit checklist - motion gaps, accessibility, color consistency, responsive, performance.
Desktop-specific UX principles - hover states, pointer precision, keyboard shortcuts, multi-window, focus management. Covers macOS, Windows, Linux, web desktop.