ShaderKit Documentation

repository·main·Indexed 19 days ago

https://github.com/jamesrochabrun/shaderkit

A Swift package providing composable Metal shaders and holographic UI effects for SwiftUI. It includes three library products: ShaderKit for shader primitives and holographic containers, ShaderKitUI for interactive components like JellySwitch and JellyButton, and ShaderCards for Pokémon-style holographic trading cards. Features include device motion tracking, a fluent CardBuilder API, and a comprehensive set of holographic styles and composition recipes for high-fidelity interactive elements.

Tokens
12.5K
Snippets
32
Records
45
Agent score
67%

What's inside ShaderKit

  1. Apply shader effects to views

    main

    Effects can be applied using two methods:

    1. Convenience Modifiers: Many effects have direct wrappers (e.g., .foil(), .glitter()).
    2. Generic Shader Modifier: Use .shader(_ effect: ShaderEffect, tilt: CGPoint? = nil, time: TimeInterval? = nil) for effects without wrappers.

    Important Rules:

    • Stacking Order: Effects wrap the view they are attached to. The order of modifiers determines the visual stack (chain order = stack order).
    • Layering: You can attach effects to specific sub-views to create a 'split-layer' effect where only certain parts of the card are shaded.
    • Manual Context: If you are using shaders outside of a HolographicCardContainer, you must manually inject context using .shaderContext(tilt:time:touchPosition:).
    CardContent()
      .foil(intensity: 0.8)
      .glitter()
      .lightSweep()
  2. Decide where to attach effects: Whole Card vs. Sub-layer

    main

    The most critical design choice is whether to attach the effect modifier chain to the entire card or just a background sub-layer. This determines how the subject interacts with the shaders.

    Attach to the Whole Card

    • Behavior: The foil/pattern shimmers over the subject as well.
    • Best for: Opaque photos and abstract cards (e.g., PsychicHoloView, GradientFoilView).
    • Warning: High-intensity patterns with 'hotspots' (like .starburst() or .galaxyHolo()) can 'burn through' the subject if applied to the whole card.

    Attach to the Background Sub-layer Only

    • Behavior: The subject stays pristine/clean while the foil shimmers around/behind it.
    • Best for: Transparent subjects (cutout PNGs). This makes the subject look like a solid object sitting in a holographic well.
    • Example: Foil+Glitter+Sweep technique.
  3. Map CSS-based Pokémon card interactions to ShaderKit

    main

    If you are migrating from or comparing with simeydotme/pokemon-cards-css, ShaderKit uses a procedural Metal implementation. The following mapping describes how CSS signals translate to ShaderKit signals:

    CSS signalShaderKit signal
    --pointer-x, --pointer-ynormalized ShaderContext.touchPosition
    --background-x, --background-ypointer-derived parallax pan
    --pointer-from-centerradial distance from the normalized pointer
    --rotate-x, --rotate-yHolographicCardContainer with .surfacePointer and a 14.3° edge limit
    --card-opacityintensity and pointer-distance response
    image foil/mask texturesprocedural noise, FBM, glitter, etching, geometry, and region-aware CardFinish layers
  4. Understand the ShaderKit composition doctrine

    main

    To create premium-looking cards, you must manage two distinct stacks: the Content Stack (the visual layers) and the Effect Stack (the shader modifiers).

    1. The Content Stack (ZStack order, bottom → top)

    1. Background: The color identity (procedural shader or LinearGradient). This serves as the fallback if an image fails to load.
    2. Subject: The user's art.
      • Transparent subject: Use a cutout PNG with .aspectRatio(contentMode: .fit). Do not clip to fill; the transparency allows the foil background to show through.
      • Opaque photo: Use .fill + .clipped(), then apply a Tint scrim (step 3).
    3. Tint scrim: For opaque photos only. A translucent LinearGradient at 0.10–0.35 opacity to marry the photo to the palette.
    4. Glass info panels + chrome: UI elements like name/title/stats on translucent backings.
    5. Border: A RoundedRectangle.strokeBorder with a 2–5 pt metallic gradient.

    2. The Effect Stack (Modifier chain, first → last)

    1. Foil / pattern base: Exactly one dominant identity (e.g., .foil(), .blendedHolo(), .starburst()).
    2. Texture / sparkle: .glitter(), .multiGlitter(), or .shimmer().
    3. Light: .lightSweep(), .radialSweep(), .glare(), or .shader(.glassSheen()). This should be LAST so reflections sit on top of everything.

    Budget Rule: Use 2–4 effects total. A proven formula is one pattern + one sparkle + one light. If layering more, lower the intensity (0.5–0.7) to avoid 'muddy' visuals.

  5. Map description hints to holographic design recipes

    main

    When generating a trading card, use the following mapping to translate user description hints into specific holographic vibes (recipes). This mapping determines the palette, effect stack, and shadowColor used in the HolographicCardContainer.

    Hint KeywordsTarget Vibe
    ice, winter, frostWinter Frost
    cosmic, space, psychic, spiritPsychic Cosmic
    gold, legendary, hero, radiantBurst Hero
    rainbow, secret, hyper, ultraSecret Rare (Cosmic Apex)
    metal, chrome, steelIndustrial Metal
    pastel, cute, softPastel Pop
    sunset, warmSunset Gradient
    tech, monoTech-Mono/Codex
    oil, dark iridescentOil-Slick
    copper, patina, greenCopper Patina
  6. Quick start with ShaderCards

    main

    To use the holographic trading card library, add the ShaderCards product to your target and import ShaderCards.

    Common Views:

    • TradingCardView(card:width:): An interactive holographic card that reacts to tilt.
    • CardGalleryView(): A browsable grid of the entire library.
    • CardFaceView(card:width:): A static, non-shader version of a card for lists or thumbnails.
    import ShaderCards
    
    // An interactive holographic card from the library
    TradingCardView(.creature(CardLibrary.emberfox), width: 300)
    
    // The whole library in a browsable grid
    NavigationStack { CardGalleryView() }
    
    // A static face for lists and thumbnails
    CardFaceView(card: .creature(CardLibrary.tidecaller), width: 160)
  7. Install the Metal Toolchain for ShaderKit

    main

    ShaderKit's foil effects are implemented as Metal shaders. In Xcode 26+, the Metal Toolchain is a separate download. If your build fails with the error cannot execute tool 'metal' due to missing Metal Toolchain, you must install it using the following command:

    xcodebuild -downloadComponent MetalToolchain

    Important: Shaders only compile correctly through Xcode's build system. Do not use swift build or swift run, as these methods copy .metal files raw, which results in all foils rendering blank. Always build using xcodebuild (or the ShaderKitDemo scheme when working within the repository).

    xcodebuild -downloadComponent MetalToolchain
  8. Quick Start with HolographicCardContainer

    main

    To create interactive holographic cards in SwiftUI, wrap your content in a HolographicCardContainer and chain shader effects like .foil(), .glitter(), or .lightSweep() onto the content. The container automatically handles device motion tracking (gyroscope), drag gestures for manual tilt, 3D rotation, dynamic shadows, and shader context injection.

    import ShaderKit
    
    HolographicCardContainer(width: 260, height: 380) {
        CardContent()
            .foil()
            .glitter()
            .lightSweep()
    }
  9. Implement glass-style info panels on cards

    main

    Do not use .ultraThinMaterial for card panels, as it samples the app window background rather than the card art. Instead, achieve the 'glass' look using a combination of translucent color fills, hairline strokes, and global scrims.

    The 'Glass' Trio Strategy:

    1. Scrims: Use global legibility scrims to ensure text is readable over complex patterns.
    2. Translucent-fill panels: Use a semi-transparent color fill for stat/name blocks.
    3. .glassSheen: Use this as a final whole-card closer to add a laminated sheen.

    Standard Info Panel Implementation:

    Text("…")                                   // plain white text
      .padding(10)
      .background(
        RoundedRectangle(cornerRadius: 12)
          .fill(.black.opacity(0.34))           // Use .white.opacity(0.55–0.75) on light cards
          .overlay(
            RoundedRectangle(cornerRadius: 12)
              .strokeBorder(.white.opacity(0.25), lineWidth: 0.5)
          )
      )
  10. Use the trading-card skill

    main

    The trading-card skill transforms an image into a holographic, Pokémon-style trading card using ShaderKit primitives. The skill is designed to be zero-interview: it takes an image and automatically invents a creature name, stats (HP, Role, Motto), and signature skills.

    Invocation Pattern

    /trading-card <image-path> [optional short description hint]
    • <image-path> (required): The path to the source image. A transparent-PNG cutout is recommended so the subject floats over the foil, though opaque photos are supported (they will receive a tint scrim).
    • description hint (optional): A few words to steer the design (e.g., "icy", "cosmic", "gold legendary", "cyberpunk"). If omitted, a design is chosen at random.
    /trading-card ~/dog.png "cosmic vibe"
  11. Map visual vibes to shader recipes and palettes

    main

    When designing card effects, map the desired 'vibe' to a specific recipe stack, color palette, and shadow color. This ensures consistent aesthetic results.

    Common Vibe Mappings

    VibeRecipe / effect stackBase paletteshadow
    Iridescent Premium (gold)R1 · base .glitter().foil()(0.95,0.85,0.5)/(0.9,0.75,0.4)/(0.85,0.7,0.35).orange
    Sunset GradientR2 · whole .foil().glitter().lightSweep()[.pink,.purple,.blue,.orange].orange
    Tech-Mono / CodexR3 · whole .foil().glitter().lightSweep() + text shadows(177,167,255)/(122,157,255)/(57,65,255) ÷255mid
    Psychic CosmicR4 · whole .foil().glitter().lightSweep() + dual scrim[.purple,.pink,.purple·0.8].purple
    Burst HeroR5 · base .starburst() + whole .radialSweep().multiGlitter()gold (1.0,0.85,0.2)….yellow
    Floating DepthR6 split-layer · .blendedHolo(0.7,0.75) + .verticalBeams()gold (0.92,0.85,0.55)….yellow
    Reverse-Holo FramedR7 · .maskedFoil/.maskedSparkle/.foilTexture (window)gold (0.95,0.9,0.6)….yellow
    Winter Frost (glacier)whole .frozen() + .lightSweep()(0.78,0.88,0.96)/(0.55,0.72,0.88)/(0.30,0.48,0.70).cyan
    Pastel Pop (bubblegum)whole .halftonePastel() + .rainbowGlitter()(0.98,0.75,0.85)/(0.85,0.80,0.95)/(0.70,0.92,0.90).pink
    Oil-Slick (interference)opaque .oilSlick() — no subject(0.015,0.02,0.045)/(0.13,0.06,0.23)/(0.02,0.14,0.18).purple
    Copper Patina (verdigris)opaque .copperPatina() — no subject(0.22,0.08,0.035)/(0.50,0.22,0.08)/(0.04,0.30,0.25).green
    Industrial Metalopaque .brushedTitanium() / .liquidMercury() / .polishedAluminum()titan (0.20,0.25,0.32)/(0.52,0.59,0.68)/(0.16,0.20,0.27).white