haplollc/ClayGlobe
haplollc/ClayGlobe has 29 GitHub stars and 0 forks. GitHot measured 0 stars in 24 hours. Last checked 2026-11-15 13:38 UTC. Last push 2026-11-06.
Measured star growth
- in 24h
- 0
- in 7 days
- Collecting history
- in 15 days
- Collecting history
- in 1 month
- Collecting history
Net star changes over overlapping periods, not daily totals. Missing measurements are left blank. Stars measure attention, not software quality.
About
A claymorphic 3D globe for SwiftUI that plants a waving flag on every country you visit
Creative & MediaREADME.md
OriginalClayGlobe
A claymorphic 3D globe for SwiftUI that plants a waving flag on every country you visit.
[](https://github.com/haplollc/ClayGlobe/stargazers)
[](LICENSE)
If ClayGlobe is useful to you, a coffee keeps it going.
Install
dependencies: [
.package(url: "https://github.com/haplollc/ClayGlobe", from: "1.0.0")
]
Try it
import SwiftUI
import ClayGlobe
struct TravelView: View {
@StateObject private var globe = GlobeManager(visited: ["USA", "FRA", "JPN"])
var body: some View {
ZStack {
GlobeNightSky()
ClayGlobeView(manager: globe)
}
}
}
If this is useful, a star helps others find it.
What you get
- A hand-modeled feel, generated in code. Every country is its own raised piece of clay on a matte plasticine sphere, with puffy clouds and a chubby little airplane circling the world.
- Flag planting with ceremony. Mark a country visited and the camera tours to it, a flag pole drops in, and the flag waves on a GPU cloth shader.
- Photo library scanning. One call reads photo location metadata (never the images), figures out every country the user has photographed in, and plants them all. Fully offline, nothing leaves the device.
- Offline reverse geocoding. Coordinate to ISO country code lookups against bundled map data, no network, no API key.
- Three looks. Orange clay, flag fill, or planted flags. U.S. states can render as their own pieces too.
- Interactive by default. Idle spin, drag to orbit, pinch to zoom, tap to squish.
Installation
Add ClayGlobe with Swift Package Manager. In Xcode: File > Add Package Dependencies and paste
https://github.com/haplollc/ClayGlobe
Or in Package.swift:
dependencies: [
.package(url: "https://github.com/haplollc/ClayGlobe", from: "1.0.0")
]
Quick start
Two lines to a living globe:
import SwiftUI
import ClayGlobe
struct TravelView: View {
@StateObject private var globe = GlobeManager(visited: ["USA", "FRA", "JPN"])
var body: some View {
ZStack {
GlobeNightSky() // optional starfield backdrop
ClayGlobeView(manager: globe)
}
}
}
Plant a flag whenever something happens in your app:
globe.plant("BRA") // camera tour + flag drop + wave
globe.plant("ITA", "EGY") // plant several at once
globe.remove("FRA") // quietly take one off
Countries are ISO 3166-1 alpha-3 codes ("JPN", "FRA", "USA"). U.S. states use USPS codes ("CA", "NY") via globe.visitedStates.
Celebrations
Freshly planted countries can erupt in a burst of their own waving emoji flags. Overlay the celebration view and it fires automatically:
ZStack {
GlobeNightSky()
ClayGlobeView(manager: globe)
.overlay { GlobeCelebration(manager: globe) }
}
Scan the photo library
Let the globe fill itself in from the user's existing photos:
Button("Find my countries") {
Task {
if let result = await globe.scanPhotoLibrary() {
print("Planted \(result.newCountries.count) new countries")
print("First trip to Japan: \(result.firstVisits["JPN"] ?? .now)")
}
}
}
The scan reads PHAsset location metadata only. No pixels are loaded, nothing is uploaded, and a library of thousands of photos resolves in seconds. Access is requested automatically; the call returns nil if the user declines.
Add the usage string to your target's Info settings:
NSPhotoLibraryUsageDescription
"Used to find countries you have visited from your photo locations. Nothing leaves your device."
Prefer to drive things yourself? VisitedCountryScanner is public:
guard await VisitedCountryScanner.requestAccess() else { return }
let findings = await VisitedCountryScanner.scan()
// findings.countries, findings.states, findings.firstVisits
Styles
globe.style = .planted // brighter green + a planted waving flag (default)
globe.style = .flags // countries filled edge to edge with their flag
globe.style = .clay // visited countries as orange clay
GlobeStyle is CaseIterable with a displayName, so a picker is one line:
Picker("Style", selection: $globe.style) {
ForEach(GlobeStyle.allCases) { Text($0.displayName).tag($0) }
}
Configuration
var config = GlobeConfiguration()
config.holdsStill = true // freeze the idle spin and camera tours
config.showsUSStates = false // keep the USA as one piece
let globe = GlobeManager(configuration: config)
holdsStill is handy for marketing captures and for honoring Reduce Motion.
Offline lookups
The bundled atlas answers geography questions with no globe on screen at all:
GlobeManager.countryCode(latitude: 35.68, longitude: 139.69) // "JPN"
GlobeManager.countryName(for: "JPN") // "Japan"
GlobeManager.flagEmoji(for: "JPN") // the Japanese flag emoji
GlobeManager.allCountryCodes // every code on the globe
How it is built
- SceneKit sphere with each country extruded from simplified boundary polygons, so the world reads as pieces of clay rather than a printed texture.
- Flags wave on the GPU. A tiny shader modifier bends each flag with overlapping sine waves pinned at the pole.
- Country resolution is ray-cast point-in-polygon over bundled boundary data, with even-odd winding so enclaves like Lesotho resolve correctly.
- The starfield is one SwiftUI
Canvaswith seeded star positions, so it animates cheaply and never re-rolls. - MVVM.
GlobeManageris the only object you talk to; views observe it.
Requirements
- iOS 17.0+
- Swift 5.9+ / Xcode 15+
License and data
ClayGlobe is available under the MIT license.
Country and state boundaries are derived from Natural Earth, a public domain map dataset. Flag imagery is rendered from the emoji set of the host OS at runtime; no flag artwork ships with the package.
Made by Haplo LLC. Extracted from Bilbo, our travel planning app, where the globe fills in as you travel.
More from Haplo
More open source from Haplo, built by the same solo developer:
- ThinkingOrbs: dotted, honestly-3D loading indicators for AI and agent interfaces in SwiftUI. Nine hand-tuned designs, two tuned sizes.
- Minted: turn any SVG into a physically-lit 3D gold medallion for SwiftUI.
- Vesalius: a map of your codebase for Claude Code, an on-device code graph with an MCP server, a CLI, a Mac app and a VS Code extension.
- MuscleMapKit: interactive 3D muscle map for iOS. Shade muscles by how hard they were worked, spin the body, tap a group.
- ProcessingField: a SwiftUI progress field, a grid of marks whose sizes describe one soft shape that drifts, folds and breathes while your app is busy.
- Gepetto: a browser agent for iOS and macOS that a person can take over at any time. Real WKWebView, small on-device models, a native SwiftUI panel.
- Mirage: one-stop on-device diffusion image-generation engine for iOS / macOS / visionOS.
- SiriGlass: the Siri drop from iOS 27, for SwiftUI. Liquid Glass that pours out of the Dynamic Island and lights up with your voice.
- Scrubbers: twelve sliders you will want to touch, for SwiftUI. One view, one enum.
- KoiPond: a living koi pond for SwiftUI. Koi glide over any view, lily pads drift on top, and a Metal shader ripples and refracts it all.