Skip to content

ZionHeZY/TitaniumFlow

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

6 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

TitaniumFlow

中文文档

A metallic particle effect demo built with SpriteKit and SwiftUI. Features dual color themes, touch interaction, and physics-based field simulation.

Preview 1 Preview 2 Preview 3

Inspiration

This project was inspired by a metallic particle effect video posted by @alexwidua on X back in 2023.

Original: https://x.com/alexwidua/status/1702356241186476225

The implementation differs from the original, but aims to recreate a similar visual effect. Thanks to Alex Widua for the creative inspiration.


How It Works

The Problem

To recreate the effect, a few challenges needed solving:

  1. Metallic appearance — particles need depth and specular highlights
  2. Square coverage — particles must fill a square region uniformly
  3. Smooth color transitions — no hard cuts when switching themes
  4. Touch interaction — particles should repel from finger contact

Grid-Based Emitters

A single SKEmitterNode with particlePositionRange produces particles that cluster at the center and thin out at edges.

The fix: divide the square into a grid, place an emitter in each cell. Every sub-region gets its own particle source, resulting in uniform coverage.

Single emitter:              Grid emitters (4×4):
     · ·                     · · · ·
    · * ·         →          · · · ·
     · ·                     · · · ·
  (center-heavy)             · · · ·
                            (uniform)

Three-Layer System

Metallic surfaces have depth and specular reflection. This is simulated with three particle layers:

Layer Grid Purpose
Base 4×4 Dark background, large particles
Mid 5×5 Main body, medium brightness
Highlight 6×6 Specular reflection, high brightness

Each layer uses the same hue with different saturation/brightness. Stacked together with additive blending, they create the metallic look.

Highlight Gradient

Real metal highlights aren't uniform — they concentrate around a focal point. An exponential falloff controls particle density:

let distance = sqrt(dx*dx + dy*dy) / maxDistance
let density = pow(1.0 - distance, gradientPower)
if density < 0.1 { continue }  // skip sparse regions

gradientPower controls falloff steepness:

Value Behavior Density at 0.5 Density at 0.8
2.0 Soft gradient 25% 4%
3.0 Moderate focus 12.5% 0.8%
5.0 Tight focus 3% 0.03%
8.0 Extreme focus 0.4% ≈0%

Higher values produce tighter, more concentrated highlights.

Color Transitions

Directly changing particleColor on emitters produces a jarring effect — existing particles keep the old color while new ones appear in the new color.

The workaround: trigger a turbulence burst to scatter particles, wait ~0.6s (peak chaos), then update the color. Old particles die off naturally while new particles emerge in the new color. No emitter rebuilding needed.

Tap color → Trigger turbulence → Wait 0.6s → Update color
                   ↓
           Old particles fade out
                   ↓
           New particles take over

Touch Interaction

SpriteKit's SKFieldNode applies forces to particles.

Finger tracking: A radialGravityField with negative strength becomes a repulsion field. Position follows the touch point.

let field = SKFieldNode.radialGravityField()
field.strength = -2.5  // negative = repel
field.position = touchLocation

Ripple trails: As the finger moves, small repulsion fields spawn along the path. Each expands and fades, creating ripple effects.

Finger path:  ○ → ○ → ○ → ○ → ●(current)
              ↓   ↓   ↓   ↓
           ripples expand and fade

Selective Field Response

If all particles respond to gravity, enabling it makes the entire square fall apart.

Solution: use fieldBitMask to make some particles ignore certain fields.

let respondsToGravity = random() > gravityVariation * 0.5
emitter.fieldBitMask = respondsToGravity ? 0xFFFFFFFF : 0xFFFFFFFB

With gravityVariation = 0.5, roughly half the particles fall while the rest stay put, creating a layered effect.

Performance

Three grids = 4×4 + 5×5 + 6×6 = 77 emitters, generating thousands of particles per second.

Optimizations:

  • Gradient pruning: Cells with density < 0.1 don't create emitters
  • Small textures: 4×4 pixel radial gradient
  • Additive blending: .add blend mode is GPU-friendly
  • Lifecycle balance: Tuned particleLifetime and particleBirthRate for stable particle count

Architecture

┌─────────────────────────────────────────────────────┐
│                   SwiftUI Layer                      │
│  ┌─────────────────────────────────────────────┐    │
│  │  ParticleHomeView                           │    │
│  │  - Top toolbar (reset / language)           │    │
│  │  - Category buttons (Size/Layer/Field/Touch)│    │
│  │  - Settings panel (Liquid Glass UI)         │    │
│  │  - Color picker                             │    │
│  └─────────────────────────────────────────────┘    │
│                        ↓ SpriteView                  │
├─────────────────────────────────────────────────────┤
│                  SpriteKit Layer                     │
│  ┌─────────────────────────────────────────────┐    │
│  │  ParticleHomeScene (SKScene)                │    │
│  │  - SKEmitterNode × N (grid emitters)        │    │
│  │  - SKFieldNode (gravity/turbulence/touch)   │    │
│  └─────────────────────────────────────────────┘    │
└─────────────────────────────────────────────────────┘

Parameters

Size

Parameter Description Default Range
Square Size Particle region side length 200 50-400
Scale Base particle size 0.8 0.1-2.0
Scale Range Size randomization 0.18 0-0.5
Lifetime Particle lifespan (seconds) 2.5 0.5-10
Lifetime Range Lifespan randomization 2.0 0-10
Fade Speed Alpha decay rate (negative) -0.3 -2~0
Speed Initial velocity 3 0-50
Speed Range Velocity randomization 3 0-30
Highlight Gradient Density falloff exponent 3.0 0-8
Highlight Target X/Y Highlight focal point 1.0/1.0 -1~1

Layer

Parameter Description Default
Base Alpha Base layer opacity 0.8
Base Birth Rate Base layer particles/sec 3000
Mid Alpha Mid layer opacity 0.7
Mid Birth Rate Mid layer particles/sec 2400
Highlight Alpha Highlight layer opacity 0.85
Highlight Birth Rate Highlight layer particles/sec 8000

Higher birth rates = denser particles = higher GPU load.

Field

Gravity

Parameter Description Default
Enabled Toggle gravity field Off
Sensor Use gyroscope for direction Off
Strength Gravity magnitude 0.2
Direction X Horizontal component (-1 left, 1 right) 0
Direction Y Vertical component (-1 down, 1 up) -1
Variation Fraction of particles affected 1.0

Turbulence

Parameter Description Default
Enabled Toggle turbulence field Off
Strength Turbulence intensity 5.0
Smoothness Noise smoothness 0.5
Animation Speed Field change rate 1.5
Position X/Y Field center 0/0
Radius Effect radius (0 = infinite) 0

Touch

Parameter Description Default
Repel Radius Touch influence radius 10
Touch Strength Repulsion force (negative = repel) -2.5
Trail Strength Ripple force along path -1.5
Ripple Spread Max ripple radius multiplier 5.0
Recovery Speed Ripple fade duration (seconds) 0.15
Trail Density Ripple spawn interval (pixels) 10
Trail Taper Ripple strength decay ratio 0.30
Expand Speed Time to reach max radius (ratio) 0.50

Localization

Supports English and Simplified Chinese via Apple's standard localization:

SpriteTestDemo/
├── en.lproj/
│   └── Localizable.strings
├── zh-Hans.lproj/
│   └── Localizable.strings

Language switches at runtime without restart.


Project Structure

TitaniumFlow/
├── TitaniumFlowApp.swift       # Entry point
├── ContentView.swift           # Root view
├── ParticleHomeScene.swift     # Core implementation
│   ├── ParticleDefaults        # Default constants
│   ├── LocalizationManager     # Language switching
│   ├── ParticleColorTheme      # Color themes
│   ├── ParticleHomeScene       # SpriteKit scene
│   ├── ParticleSettings        # Settings model
│   └── ParticleHomeView        # Main SwiftUI view
├── en.lproj/                   # English strings
├── zh-Hans.lproj/              # Chinese strings
└── Assets.xcassets/            # Assets

Requirements

  • iOS 26+
  • SwiftUI
  • SpriteKit
  • CoreMotion

License

MIT

About

A metallic particle effect demo built with SpriteKit and SwiftUI.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages