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

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages