Skip to content

mIbrahim017/3d-globe-map

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

9 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

🌐 Globe3D

A lightweight, highly-configurable interactive 3D globe built with Three.js and Canvas 2D.
No external map dependencies β€” everything is self-contained in a single HTML file. Designed to embed directly in iOS (WKWebView), Android (WebView), or any web page.

License: MIT Three.js r128 Zero Dependencies


Demo

Live Demo

πŸ‘‰ https://mibrahim017.github.io/3d-globe-map/


✨ Features

  • 🌍 All 250+ countries rendered from SVG paths directly onto a 3D sphere texture
  • 🎨 Fully themeable β€” colors, ocean, grid, glow, stroke, all configurable
  • πŸ“± Mobile-first β€” adaptive texture resolution, touch drag, pinch-zoom
  • πŸ”­ Smooth camera β€” animated zoom-to-country, slerp rotation, inertia-free drag
  • πŸ“‘ Native bridge β€” iOS WKWebView, Android JSInterface, and web window callbacks
  • ⚑ Zero npm β€” single HTML file, one Three.js CDN import
  • πŸ›  Rich JS API β€” colorize countries, swap SVGs, control camera programmatically

πŸš€ Quick Start

Web (iframe)

<iframe src="src/globe3d.html" style="width:100%;height:500px;border:none"></iframe>

Web (direct)

Just open src/globe3d.html in any modern browser.

iOS (WKWebView)

let webView = WKWebView(frame: view.bounds, configuration: config)
let url = Bundle.main.url(forResource: "globe3d", withExtension: "html")!
webView.loadFileURL(url, allowingReadAccessTo: url.deletingLastPathComponent())

Android (WebView)

webView.settings.javaScriptEnabled = true
webView.addJavascriptInterface(MyBridge(), "Android")
webView.loadUrl("file:///android_asset/globe3d.html")

πŸ—‚ Project Structure

globe3d/
β”œβ”€β”€ src/
β”‚   β”œβ”€β”€ globe3d.html                      # ← The globe (self-contained)
β”‚   └── three.min.js                      # Three.js r128 (copy from CDN or npm)
β”œβ”€β”€ demo/
β”‚   └── index.html                        # Full interactive configuration demo (8 tabs)
β”œβ”€β”€ examples/
β”‚   β”œβ”€β”€ android/
β”‚   β”‚   β”œβ”€β”€ README.md                     # Android setup guide
β”‚   β”‚   └── app/src/main/
β”‚   β”‚       β”œβ”€β”€ AndroidManifest.xml
β”‚   β”‚       β”œβ”€β”€ assets/                   # ← copy globe3d.html + three.min.js here
β”‚   β”‚       β”œβ”€β”€ java/com/globe3d/example/
β”‚   β”‚       β”‚   β”œβ”€β”€ GlobeActivity.java    # Java integration (full example)
β”‚   β”‚       β”‚   └── GlobeActivityKt.kt   # Kotlin integration (full example)
β”‚   β”‚       └── res/layout/
β”‚   β”‚           └── activity_globe.xml   # WebView layout with overlay UI
β”‚   └── ios/
β”‚       β”œβ”€β”€ README.md                     # iOS setup guide
β”‚       └── Globe3DExample/
β”‚           β”œβ”€β”€ GlobeViewController.swift # UIKit / Swift (full example)
β”‚           β”œβ”€β”€ GlobeSwiftUI.swift        # SwiftUI wrapper + GlobeController
β”‚           β”œβ”€β”€ GlobeViewController.m     # UIKit / Objective-C (full example)
β”‚           └── GlobeViewController.h    # Objective-C header
β”œβ”€β”€ docs/
β”‚   └── API.md                            # Complete API reference
β”œβ”€β”€ README.md
└── LICENSE

Note: globe3d.html references three.min.js via <script src="three.min.js">.
Copy it from cdnjs into src/.


βš™οΈ Configuration Reference

All configuration lives in clearly marked constant blocks at the top of globe3d.html.
Edit them before deploy, or override them at runtime via the JS API.

🎨 Colors

// ── Colour config ──────────────────────────────────────────────────
var COUNTRY_FILL   = '#2a6496';          // Default country fill
var COUNTRY_HOVER  = '#4a9fd4';          // Hovered country fill
var COUNTRY_STROKE = 'rgba(0,0,0,0.35)'; // Country border color
var SELECT_STROKE  = 'rgba(255,255,255,0.9)'; // Selected country border

🌊 Ocean & Grid

The ocean is drawn as a Canvas 2D linear gradient inside buildBg():

function buildBg(){
  var g = _bgCtx.createLinearGradient(0, 0, 0, TEX_H);
  g.addColorStop(0,   '#8ee0d8');   // ← Ocean top color
  g.addColorStop(.5,  '#6dccc4');   // ← Ocean mid color
  g.addColorStop(1,   '#8addd6');   // ← Ocean bottom color
  _bgCtx.fillStyle = g;
  _bgCtx.fillRect(0, 0, TEX_W, TEX_H);

  // Grid lines
  _bgCtx.strokeStyle = 'rgba(255,255,255,0.18)'; // ← grid opacity
  _bgCtx.lineWidth   = 0.4;
  // ... draws 30Β° graticule
}

πŸ”­ Camera & Zoom

var FOV        = isMobile ? 55 : 42;   // Field of view (degrees)
var GLOBE_R    = 1.0;                  // Globe radius (Three.js units)

// Auto-computed from FOV β€” override if needed:
var FIT_Z      = GLOBE_R / Math.sin(_fovRad / 2);   // camera distance = full fit
var NEAR_Z     = FIT_Z * 0.68;                       // zoom-in distance (68% of fit)
var ZOOM_OUT_Z = FIT_Z * 1.05;                       // default zoom-out (slight pull-back)
var ZOOM_IN_Z  = NEAR_Z;                             // used when country is selected

var MIN_Z = 0.5;   // Hard minimum zoom (scroll/pinch limit)
var MAX_Z = 5.5;   // Hard maximum zoom

πŸ”„ Rotation

var autoSpeed = 0.15;   // Rotation speed (0 = stopped, 1.5 = fast)
// Direction: always around Y-axis (east→west). To reverse, negate autoSpeed.

πŸ“ Texture Quality

var TEX_W = isMobile ? 2048 : 4096;   // Texture width in pixels
var TEX_H = isMobile ? 1024 : 2048;   // Texture height in pixels

// Globe mesh segments (higher = smoother sphere):
var globe = new THREE.Mesh(
  new THREE.SphereGeometry(GLOBE_R, isMobile?56:80, isMobile?40:56),
  globeMat
);

πŸ›  JavaScript API

All functions are globally accessible on window (or iframe.contentWindow).

Color & Setup

Function Description
setCountryColor(id, color) Set fill of one country by ISO-3166-1 alpha-2 code
colorize(pathOrId, color, strokeColor?) Alias with optional stroke
setupCountry(ids[], selColor, fill, bg, stroke) Batch setup β€” selected countries + all colors
setupCountries(ids[], selColor, fgColor) Lighter version without background
loadSVG(rawSVG, ids[], selColor, bg, fg) Replace SVG paths at runtime
setMapBackground(color) Change body/canvas background
getRandomColor() Returns a random hex color string

Camera & Navigation

Function Description
zoomToCountry(idOrElement) Smooth-rotate globe so country faces camera (+Z)
animateZoom(targetZ, onDone?) Ease camera to Z distance over 40 frames
setZoom(z) Instantly set camera Z (clamped to MIN_Z..MAX_Z)
zoomIn() Step in by 0.4 units
zoomOut() Step out by 0.4 units
pauseGlobe() Stop auto-rotation
resumeGlobe() Resume auto-rotation
resetView() Reset orientation to identity + zoom out
deselectAll() Clear selection and restore rotation

Selection

Function Description
toggleCountry(pathElement) Select/deselect a country (internal, but safe to call)
sel Set<string> of currently selected country IDs
customColors { [id]: hexColor } β€” override per-country fill
customStrokes { [id]: color } β€” override per-country stroke

πŸ“‘ Native Bridge Events

Globe fires events to three bridge types simultaneously. Implement whichever fits your platform:

Web (recommended for iframe usage)

// Inject before / after globe loads:
iframe.contentWindow.NativeBridge = {
  didTapPath: function(jsonString) {
    var data = JSON.parse(jsonString);
    // data = { id, selected, centroid:{x,y}, bounds:{x,y,width,height}, color, stroke }
    console.log('Tapped:', data.id, data.selected);
  }
};

Startup callback

// In parent page β€” called ~200ms after globe init:
window.onGlobeReady = function() {
  console.log('Globe is ready');
};

iOS (WKWebView)

// In WKScriptMessageHandler:
func userContentController(_ ucc: WKUserContentController,
                           didReceive message: WKScriptMessage) {
    if message.name == "didTapPath" {
        let json = message.body as! String
        // parse json: { id, selected, centroid, bounds, color, stroke }
    }
    if message.name == "globeReady" {
        // globe is loaded and ready
    }
}
// Register handlers:
config.userContentController.add(self, name: "didTapPath")
config.userContentController.add(self, name: "globeReady")

Calling back into the globe from Swift:

webView.evaluateJavaScript("setCountryColor('US','#ff6b35')")
webView.evaluateJavaScript("zoomToCountry('JP')")

Android (WebView + JSInterface)

class GlobeBridge {
    @JavascriptInterface
    fun onCountryTapped(json: String) {
        val data = JSONObject(json)
        val id = data.getString("id")
        val selected = data.getBoolean("selected")
        // handle on UI thread:
        runOnUiThread { handleCountryTap(id, selected) }
    }

    @JavascriptInterface
    fun onGlobeReady() {
        // Globe initialized
    }
}
webView.addJavascriptInterface(GlobeBridge(), "Android")

Calling back into the globe:

webView.evaluateJavascript("setCountryColor('CN','#e63946')", null)

🎨 Theming Examples

Dark Cyberpunk

COUNTRY_FILL   = '#1a0a2e';
COUNTRY_STROKE = 'rgba(123,47,255,0.7)';
// Ocean:
g.addColorStop(0, '#0a0020');
g.addColorStop(1, '#050015');
document.body.style.background = '#050505';

Warm Political Map

// Color each continent a distinct color
['US','CA','MX'].forEach(id => setCountryColor(id, '#4361ee'));
['CN','IN','JP'].forEach(id => setCountryColor(id, '#e76f51'));
['DE','FR','GB'].forEach(id => setCountryColor(id, '#457b9d'));

Heatmap (data-driven)

// countries = [{id:'US', value:0.9}, {id:'CN', value:0.7}, ...]
function heatColor(t) {
  var r = Math.round(255 * t);
  var b = Math.round(255 * (1 - t));
  return 'rgb('+r+',50,'+b+')';
}
countries.forEach(c => setCountryColor(c.id, heatColor(c.value)));
needRedraw = true;

πŸ“ Payload Schema

Every country tap fires this JSON:

{
  "id":       "US",             // ISO 3166-1 alpha-2
  "selected": true,             // toggle state
  "centroid": { "x": 203.4, "y": 312.1 },  // SVG coordinate centroid
  "bounds": {
    "x": 50.2, "y": 290.0,
    "width": 310.5, "height": 120.3        // SVG bounding box
  },
  "color":  "#2a6496",          // current fill (null if default)
  "stroke": null                // current stroke override (null if default)
}

🌐 Browser Support

Browser Support
Chrome 80+ βœ… Full
Safari 14+ βœ… Full
Firefox 80+ βœ… Full
iOS WKWebView 14+ βœ… Full
Android WebView (Chrome 80+) βœ… Full
Samsung Internet 12+ βœ… Full

Requires: WebGL, Canvas 2D, ES6 (Set, const, arrow functions polyfilled for ES5 targets).


πŸ“ Adding three.min.js

The globe references <script src="three.min.js">.
You can get it in several ways:

# Option 1: Download directly
curl -o src/three.min.js \
  https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js

# Option 2: npm
npm install three@0.128.0
cp node_modules/three/build/three.min.js src/

# Option 3: Change to CDN in globe3d.html (no local file needed)
# Replace: <script src="three.min.js">
# With:    <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js">

🀝 Contributing

  1. Fork the repo
  2. Create a branch: git checkout -b feature/my-feature
  3. Make changes to src/globe3d.html or demo/
  4. Open a PR with a clear description

Areas welcome for contribution:

  • Additional country data layers (population, GDP choropleth)
  • Star/atmosphere shader layer
  • Marker/pin system (lat/lng β†’ 3D point)
  • Offline SVG swap for custom regions
  • React / Vue wrapper component

πŸ“„ License

MIT β€” free for personal and commercial use. See LICENSE.


πŸ™ Credits

  • Three.js β€” 3D rendering engine (r128)
  • SVG world map paths adapted from Natural Earth public domain data
  • Built with ❀️ for cross-platform native + web use

About

Interactive 3D world globe map with selectable countries designed for iOS and Android WebView integration.

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages