athevon/genjutsu4 files

Swiftui Graphics

Advanced SwiftUI visuals - Metal shaders (.colorEffect, .layerEffect, .distortionEffect), .visualEffect, Liquid Glass (iOS 26), Canvas, holographic and CRT effects.

Specification
Skill ID
athevon/genjutsu/swiftui-graphics
Publisher
athevon
Repository
genjutsu
Installs
0
Files
4
Synced
Sep 23, 2026
How to use it

Open any RiverX project, open the Skills panel in the chat, and search for this identifier. The files are fetched from the source repository at install time.

athevon/genjutsu/swiftui-graphicsInstalls these files
  • SKILL.md
  • references/canvas-swiftui.md
  • references/liquid-glass-deep.md
  • references/metal-recipes.md

What this skill tells the agent

Version-sensitive. Every API name, SDK gate and browser-support claim below was verified on 2026-09-08 against primary sources. What against, and when, is in _jutsu/VERSIONS.md. If that date is old, re-verify before acting on a version number.

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?

NeedAPI
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 dumpCanvas 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. Availability: iOS 17.0+, iPadOS 17.0+, Mac Catalyst 17.0+, macOS 14.0+, tvOS 17.0+, visionOS 1.0+ - not watchOS. Below those, 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.

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
                }
            }
    }
}
// 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.

// 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);
}
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.

// 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);
}
.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.

ScrollView {
    LazyVStack(spacing: 16) {
        ForEach(items) { item in
            CardView(item: item)
                .visualEffect { content, proxy in
                    let y = proxy.frame(in: .scrollView).minY
                    let scale = scale(for: y)
                    let opacity = opacity(for: y)
                    return content
                        .scaleEffect(scale)
                        .opacity(opacity)
                }
        }
    }
}

func scale(for y: CGFloat) -> CGFloat {
    let progress = max(0, min(1, y / 600))
    return 0.85 + progress * 0.15
}

Use cases:

  • Parallax cards (move slower than scroll)
  • Scroll-driven scale (cards grow as they enter view)
  • Sticky reveal (clamp position via .offset(y: max(0, -y)))
.visualEffect is purely visual: the geometry it returns is read-only and the modifier cannot trigger state updates. Don't try to write to @State from inside.

Liquid Glass (iOS 26)

System glassmorphism with adaptive depth and morphing transitions. Built into the OS, optimized at the system level.

struct HeroCard: View {
    // @Namespace is a property wrapper - it has to live inside the View, not at file scope.
    @Namespace private var glassNS

    var body: some View {
        if #available(iOS 26.0, macOS 26.0, *) {
            // glassEffectID only does anything inside a GlassEffectContainer.
            GlassEffectContainer(spacing: 20.0) {
                Label("Hero", systemImage: "sparkles")
                    .font(.title2)
                    .padding()

Use this skill in a project

Open any RiverX project, search the Skills panel for athevon/genjutsu/swiftui-graphics, and install it in one click.

Use this skill