SwiftUIShaders

repository·main·Indexed 42 days ago

https://github.com/krispuckett/swiftuishaders

A collection of 41 Metal-based shaders for SwiftUI, providing complex visual effects like holographic foils and glitch effects as single-line View modifiers. Includes 37 animated and 4 static shaders using the bcs_ namespace prefix, designed for use with images, text, and other views.

Tokens
3.5K
Snippets
4
Records
6
Agent score
6%

What's inside SwiftUIShaders

  1. How SwiftUIShaders modifiers work

    main

    Each shader wrapper applies a Metal layerEffect. The following parameters are handled by the wrapper:

    • position, layer: Handled automatically by SwiftUI.
    • size: The view's pixel size, provided via GeometryProxy.
    • time: Elapsed seconds, provided via TimelineView(.animation) (for animated shaders only).
    • interaction point (e.g. touchPos): A UnitPoint (0...1) passed by the user and converted to pixels.
    • Other parameters: Typed Float parameters provided by the user with default values.

    Note on maxSampleOffset: Every method accepts a maxSampleOffset: parameter (defaulting to 64×64). Increase this if heavy displacement effects appear clipped at the edges, or decrease it to improve performance on simpler effects.

  2. Install SwiftUIShaders via Swift Package Manager

    main

    To use SwiftUIShaders in your project, add it as a dependency using Swift Package Manager.

    In Xcode, go to File ▸ Add Package Dependencies and enter the repository URL: https://github.com/krispuckett/SwiftUIShaders.git. Alternatively, add it directly to your Package.swift file.

    Important Requirement: You must build your project using Xcode or xcodebuild. Do not use swift build via the command line, as the SwiftPM command-line driver does not run the Metal toolchain required to produce the default.metallib file.

    .package(url: "https://github.com/krispuckett/SwiftUIShaders.git", from: "1.0.0")
  3. Quick start with SwiftUIShaders modifiers

    main

    SwiftUIShaders provides a collection of Metal shaders exposed as SwiftUI View modifiers. Most shaders are animated and run automatically using TimelineView(.animation), meaning you do not need to manage timers.

    Each shader has sensible default values, so you can apply them without arguments. For best results, apply these shaders to views containing rich content like photos, illustrations, or text; they may not produce visible effects on flat, single-color fills.

    import SwiftUI
    import SwiftUIShaders
    
    struct ContentView: View {
        var body: some View {
            Image("book-cover")
                .resizable()
                .scaledToFit()
                .bcsHolographic(intensity: 0.6, scale: 12, speed: 1.2)
        }
    }
  4. Performance considerations for SwiftUIShaders

    main

    SwiftUIShaders are GPU fragment effects. While most are efficient, keep the following in mind:

    • Heavy Effects: Shaders involving significant blur or displacement (e.g., .bcsFrosted, .bcsShatterGlass, .bcsMagneticField) sample many neighboring pixels. Profile these carefully if applying them to large, fast-scrolling views.
    • Animated Shaders: These re-render every frame while visible. Avoid running dozens of animated shaders off-screen.
    • Precision: The library uses half-precision for speed. You may notice slight color banding at extreme parameter values.
    • Optimization: Use the maxSampleOffset: parameter to lower the sampling cost for simpler effects.
  5. Use interaction-driven shaders with touch input

    main

    Some shaders, specifically .bcsTouchRipple and .bcsRefractLens, are interaction-driven and require a touchPos: UnitPoint. To implement this, use a GeometryReader to convert a drag location into a normalized UnitPoint (0...1) and pass it to the modifier.

    @State private var touch: UnitPoint = .center
    
    GeometryReader {
        myView
            .bcsRefractLens(touchPos: touch, lensRadius: 0.3)
            .gesture(DragGesture(minimumDistance: 0).onChanged { v in
                touch = UnitPoint(x: v.location.x / geo.size.width,
                                  y: v.location.y / geo.size.height)
            })
    }
  6. Reference: Available SwiftUIShaders

    main

    The library contains 41 shaders. 37 are animated and 4 are static. All modifiers use the bcs_ namespace prefix.

    | Shader | Type | Parameters |
    |---|---|---|
    | **`.bcsAurora`** | animated | `intensity` (0–1), `bands` (1–8), `speed` (0.3–3), `colorShift` (0–1) |
    | **`.bcsBlackHole`** | animated | `mass` (0.05–0.5), `spin` (0–5), `distortion` (10–200), `ringBrightness` (0–2) |
    | **`.bcsChromaticSplit`** | animated | `spread` (0–30), `angle` (0–6.28), `edgeOnly` (0–1), `animate` (0–1) |
    | **`.bcsDatamosh`** | animated | `blockCorruption` (0–1), `smearAmount` (0–6), `colorBleed` (0–1), `glitchRate` (0.5–5) |
    | **`.bcsDisintegrate`** | animated | `threshold` (0–1), `edgeWidth` (0.05–0.3), `driftAmount` (0–50), `direction` (0–6.28) |
    | **`.bcsDuochrome`** | animated | `intensity` (0–1), `hue1` (0–1), `hue2` (0–1), `contrast` (0.5–2) |
    | **`.bcsEcho`** | animated | `echoCount` (2–8), `spread` (5–50), `direction` (0–6.28), `fade` (0.3–0.9) |
    | **`.bcsEmboss`** | static | `strength` (0–5), `angle` (0–6.28), `mixAmount` (0–1) |
    | **`.bcsEtherealAura`** | animated | `auraWidth`, `auraIntensity`, `pulseSpeed`, `distortion`, `hueShift` |
    | **`.bcsFrosted`** | static | `frostAmount` (0–1), `grainSize` (1–20), `clearRadius` (0–1), `clearSoftness` (0–1) |
    | **`.bcsGeometricWarp`** | animated | `spiralTight` (1–8), `zoomRepeat` (0.3–2), `rotation` (0–6.28), `blend` (0–1) |
    | **`.bcsGlitch`** | animated | `intensity` (0–1), `blockSize` (2–50), `scanLines` (0–1), `colorShift` (0–20) |
    | **`.bcsGravityWells`** | animated | `wellStrength` (10–200), `wellCount` (1–5), `orbitSpeed` (0.1–3), `warpFalloff` (0.5–5) |
    | **`.bcsHeatShimmer`** | animated | `amplitude` (0–20), `frequency` (1–30), `speed` (0.5–5), `verticalBias` (0–1) |
    | **`.bcsHolographic`** | animated | `intensity` (0–1), `scale` (1–20), `speed` (0.1–3), `angleOffset` (0–6.28) |
    | **`.bcsInkBleed`** | animated | `warpStrength` (0–5), `scale` (1–10), `speed` (0.1–2), `detail` (2–8) |
    | **`.bcsKaleidoscope`** | animated | `segments` (2–16), `rotation` (0–6.28), `zoom` (0.5–3), `animateSpeed` (0–2) |
    | **`.bcsLiquidChrome`** | animated | `distortion` (0–30), `chromeIntensity` (0–1), `flowSpeed` (0.1–3), `reflectionScale` (1–10) |
    | **`.bcsLiquidMirror`** | animated | `mirrorAxis` (0.3–0.7), `ripple` (2–30), `speed` (0.5–3), `depth` (0–1) |
    | **`.bcsLiveRipple`** | animated | `amplitude` (0–30), `frequency` (5–60), `speed` (1–10), `damping` (0.5–5), `ringCount` (1–5) |
    | **`.bcsMagneticField`** | animated | `fieldStrength` (5–80), `lineCount` (3–20), `fieldTurbulence` (0–1), `polarity` (0–1) |
    | **`.bcsMelt`** | animated | `meltAmount` (0–100), `dripScale` (1–15), `speed` (0.1–3), `heat` (0–1) |
    | **`.bcsMorphBreathe`** | animated | `breatheDepth` (5–50), `breatheRate` (0.3–3), `warpComplexity` (1–8), `organic` (0–1) |
    | **`.bcsNeonEdge`** | animated | `edgeStrength` (1–10), `glowAmount` (0–2), `colorCycle` (0–3), `mixOriginal` (0–1) |
    | **`.bcsPixelateMosaic`** | animated | `pixelSize` (4–60), `bevel` (0–1), `animateAssemble` (0–1), `gap` (0–0.3) |
    | **`.bcsPixelateStorm`** | animated | `pixelSize` (2–40), `stormAmount` (0–1), `swirl` (0–3), `pulse` (0–3) |
    | **`.bcsPlasma`** | animated | `intensity` (0–1), `scale` (1–10), `speed` (0.5–5), `colorMode` (0–1) |
    | **`.bcsPulse`** | animated | `amplitude` (0–30), `bpm` (30–180), `sharpness` (1–10), `glowIntensity` (0–1) |
    | **`.bcsRefractLens`** | static | `touchPos` (UnitPoint), `lensRadius` (0.1–0.5), `refraction` (1–3), `aberration` (0–15), `wobble` (0–1) |
    | **`.bcsShatter`** | animated | `shardCount` (3–30), `explode` (0–1), `rotationAmt` (0–3), `edgeGlow` (0–2) |
    | **`.bcsShatterGlass`** | animated | `crackDensity` (3–15), `glassRefraction` (0–20), `prismStrength` (0–1), `shatterSpread` (0–1) |
    | **`.bcsShockwave`** | animated | `waveSpeed` (50–500), `ringWidth` (5–60), `strength` (5–80), `repeatRate` (0.5–5) |
    | **`.bcsSmokeReveal`** | animated | `smokeAmount` (0–1), `smokeScale` (2–10), `windSpeed` (0.5–3), `smokeTurb` (0.5–3) |
    | **`.bcsSolarize`** | animated | `threshold` (0.2–0.8), `curveIntensity` (0–3), `colorSeparation` (0–1), `animate` (0–1) |
    | **`.bcsThermal`** | animated | `intensity` (0–1), `shimmer` (0–15), `noiseSpeed` (0.5–3), `paletteShift` (0–1) |
    | **`.bcsTopographic`** | animated | `lineCount` (5–40), `lineWidth` (0.01–0.15), `colorize` (0–1), `animate` (0–1) |
    | **`.bcsTouchRipple`** | static | `touchPos` (UnitPoint), `touchAge`, `amplitude` (0–30), `frequency` (5–40), `speed` (50–500), `decay` (0.5–4) |
    | **`.bcsUnderwaterCaustics`** | animated | `causticScale` (2–15), `causticIntensity` (0–2), `waterDistortion` (0–30), `waterDepth` (0–1) |
    | **`.bcsVortex`** | animated | `twistAmount` (0–10), `radius` (0.1–1), `speed` (0.1–3), `falloff` (0.5–5) |
    | **`.bcsWavePool`** | animated | `amplitude` (0–25), `wavelength` (5–40), `speed` (0.5–5), `complexity` (1–6) |
    | **`.bcsWormhole`** | animated | `depth` (1–8), `speed` (0.3–3), `twist` (0–5), `radius` (0.1–0.5) |