A pure Swift/Metal port of cobe — the lightweight WebGL globe library by Shu Ding. Renders a beautiful interactive 3D globe with dotted land masses, markers, and arcs, all running on the GPU via Metal shaders.
Credit: This project is a faithful port of cobe's rendering pipeline to Apple's Metal framework. The Fibonacci lattice dot algorithm, rotation math, and world map texture are directly derived from cobe. All credit for the original concept and implementation goes to Shu Ding and the cobe contributors.
- Metal GPU rendering — Fibonacci lattice dot algorithm ported from cobe's GLSL shaders
- Cobe's actual world map — the same base64-encoded equirectangular PNG texture
- Markers — glowing circles at geographic coordinates with back-face culling
- Arcs — quadratic Bezier curves connecting two points, elevated above the surface
- Drag to rotate — pan gesture with momentum/inertia and auto-rotation
- Dark & light modes — matching cobe's
darkparameter for both aesthetics - SwiftUI native — simple
GlobeView(configuration:)API - Cross-platform — iOS 16+ and macOS 13+
- Zero dependencies — pure Swift Package, no external libraries
- Runtime shader compilation — no
.metalfiles needed, works with SPM out of the box
Add to your Package.swift dependencies:
.package(url: "https://github.com/ohwhen/CobeKit.git", from: "1.0.0")Or in Xcode: File → Add Package Dependencies → paste the repo URL.
import SwiftUI
import CobeKit
struct ContentView: View {
@State private var config = GlobeConfiguration()
var body: some View {
GlobeView(configuration: $config)
.frame(width: 400, height: 400)
.onAppear {
config.theta = 0.3
config.dark = 1.0
config.baseColor = SIMD3(0.3, 0.3, 0.6)
config.glowColor = SIMD3(0.1, 0.1, 0.6)
}
}
}config.markers = [
GlobeMarker(location: SIMD2(40.71, -74.01), size: 0.08, color: SIMD3(1, 0.5, 0.1)), // NYC
GlobeMarker(location: SIMD2(51.51, -0.13), size: 0.06, color: SIMD3(1, 0.5, 0.1)), // London
GlobeMarker(location: SIMD2(35.68, 139.69), size: 0.07, color: SIMD3(1, 0.5, 0.1)), // Tokyo
]config.arcs = [
GlobeArc(from: SIMD2(40.64, -73.78), to: SIMD2(51.47, -0.46), color: SIMD3(0.3, 0.7, 1)), // JFK → LHR
GlobeArc(from: SIMD2(51.47, -0.46), to: SIMD2(25.25, 55.36), color: SIMD3(0.3, 0.7, 1)), // LHR → DXB
]| Property | Type | Default | Description |
|---|---|---|---|
phi |
Float |
0 |
Horizontal rotation (radians) |
theta |
Float |
0.3 |
Vertical tilt (radians) |
mapSamples |
Float |
16000 |
Number of dots on the globe |
mapBrightness |
Float |
6.0 |
Dot brightness multiplier |
mapBaseBrightness |
Float |
0.05 |
Minimum dot brightness (ocean visibility) |
baseColor |
SIMD3<Float> |
(0.3, 0.3, 0.6) |
Globe surface color |
glowColor |
SIMD3<Float> |
(0.3, 0.3, 0.8) |
Atmospheric glow color |
markerColor |
SIMD3<Float> |
(1.0, 0.5, 0.2) |
Default marker color |
arcColor |
SIMD3<Float> |
(0.3, 0.6, 1.0) |
Default arc color |
diffuse |
Float |
1.2 |
Diffuse lighting intensity |
dark |
Float |
1.0 |
Dark mode (1.0) vs light mode (0.0) |
opacity |
Float |
1.0 |
Globe opacity |
scale |
Float |
1.0 |
Zoom factor |
autoRotateSpeed |
Float |
0.005 |
Auto-rotation speed (0 to disable) |
dragEnabled |
Bool |
true |
Enable drag-to-rotate gesture |
The Example/ directory contains a full iOS app with 5 demo tabs:
- Dark Globe — classic dark blue cobe aesthetic
- Neon — cyberpunk hot pink glow with green base
- Markers — 10 city markers with tap-to-rotate
- Flights — airport markers with arc connections (SFO → JFK → AMS → DXB → SYD)
- Whop — branded landing page hero with animated entrance
cd Example
xcodegen generate # requires xcodegen
open CobeKitExample.xcodeprojThen select an iPhone simulator and run.
Sources/CobeKit/
├── GlobeView.swift # SwiftUI wrapper + drag/inertia coordinator
├── GlobeRenderer.swift # Metal renderer (MTKViewDelegate)
├── GlobeConfiguration.swift # Public config API, markers, arcs
└── ShaderSource.swift # Metal shaders (compiled at runtime)
Three Metal render passes per frame, matching cobe's WebGL architecture:
- Globe pass — full-screen triangle, fragment shader does ray-sphere intersection + Fibonacci lattice dot placement + texture lookup + lighting/glow
- Marker pass — instanced quads positioned at geographic coordinates, back-face culled
- Arc pass — instanced triangle strips along quadratic Bezier curves
The main porting challenge was Metal's fragment coordinate system (y=0 at top) vs WebGL's (y=0 at bottom). This affects:
- The globe's view-space Y direction (inverted in Metal)
- Marker/arc vertex shaders need to negate Y when converting view-space → clip-space
- Vertical drag direction is negated to compensate
This project would not exist without cobe by Shu Ding. The Fibonacci lattice algorithm, van der Corput bit-reversal sequence, rotation math, lighting model, and world map texture are all faithfully ported from cobe's original GLSL shaders. Thank you for creating such an elegant and beautiful library.
MIT
