# PyGlobeGl with Panel

**URL:** <https://discourse.holoviz.org/t/pyglobegl-with-panel/9094>\
**Category:** Showcase\
**Created:** [February 7, 2026, 6:22am UTC](https://discourse.holoviz.org/t/pyglobegl-with-panel/9094 "2026-02-07T06:22:00Z")\
**Posts on this page:** 1\
**Page:** 1

<div class="post-metadata">

**Author:** ![Marc](https://discourse.holoviz.org/user_avatar/discourse.holoviz.org/marc/32/24_2.png) [@Marc](https://discourse.holoviz.org/u/Marc)\
**Post date:** [February 7, 2026, 6:22am UTC](https://discourse.holoviz.org/t/pyglobegl-with-panel/9094/1 "2026-02-07T06:22:00Z")

</div>

# Playing with PyGlobeGL + Panel

I came across [PyGlobeGL](https://github.com/owenlamont/pyglobegl) recently and had to try it with Panel. It’s an anywidget wrapper for [globe.gl](https://globe.gl/) that lets you create interactive 3D globes in Python.

What I like about it:

- Works nicely with Panel via `pn.pane.IPyWidget`
- Supports hover/click callbacks, so you can wire it up to other Panel components
- Has different layer types (points, arcs, polygons, heatmaps, etc.)

## Installation

```bash
uv add panel wachfiles ipywidgets_bokeh setuptools pyglobegl 

```

Note: `setuptools` is needed until [ipywidgets\_bokeh#125](https://github.com/bokeh/ipywidgets_bokeh/issues/125) has been resolved.

## Getting Started

Here’s a minimal example - just two cities on a globe:

```python
import panel as pn
from pyglobegl import GlobeConfig, GlobeLayerConfig, GlobeWidget, PointDatum, PointsLayerConfig

pn.extension("ipywidgets")

points = [
    PointDatum(lat=51.5, lng=-0.1, color="#ff6b6b", label="London", radius=0.8),
    PointDatum(lat=40.7, lng=-74.0, color="#00ffff", label="New York", radius=0.8),
]

config = GlobeConfig(
    globe=GlobeLayerConfig(
        globe_image_url="https://cdn.jsdelivr.net/npm/three-globe/example/img/earth-blue-marble.jpg"
    ),
    points=PointsLayerConfig(points_data=points),
)
globe = GlobeWidget(config=config)

pn.pane.IPyWidget(globe, height=800, width=800).servable()

```

Run it with `panel serve hello_globe.py` - you can drag to rotate and scroll to zoom.

![panel-pyglobegl-basic](https://discourse.holoviz.org/uploads/default/original/2X/a/a827e432e2395b11bda43e706801832b9602b9c8.gif)

## A Bigger Example: Flight Network

I put together a more complete example with flight arcs connecting cities, a dark theme, and event callbacks that update a status line when you hover:

![panel-pyglobegl-advanced-ezgif.com-optimize](https://discourse.holoviz.org/uploads/default/original/2X/b/bbfbef2b4c975379b29104bd405398bf7b04d6e4.gif)

> **Click to expand full code**
>
> ```python
> """Global Flight Network - PyGlobeGL Demo."""
> 
> import panel as pn
> from pyglobegl import (
> ArcDatum,
> ArcsLayerConfig,
> GlobeConfig,
> GlobeLayerConfig,
> GlobeWidget,
> PointDatum,
> PointsLayerConfig,
> )
> 
> # Major world cities (lat, lng, name, color)
> cities = [
> (40.7, -74.0, "New York", "#00ffff"),
> (51.5, -0.1, "London", "#ff6b6b"),
> (35.7, 139.7, "Tokyo", "#ffd93d"),
> (1.3, 103.8, "Singapore", "#6bcb77"),
> (-33.9, 151.2, "Sydney", "#9b59b6"),
> (25.3, 55.3, "Dubai", "#ff9f43"),
> (48.9, 2.3, "Paris", "#ff6b9d"),
> (-23.5, -46.6, "Sao Paulo", "#54a0ff"),
> ]
> 
> # Create city points
> points = [
> PointDatum(lat=lat, lng=lng, altitude=0.02, color=color, label=name, radius=0.8)
> for lat, lng, name, color in cities
> ]
> 
> # Create flight arcs between cities
> arcs = [
> ArcDatum(
> start_lat=40.7,
> start_lng=-74.0,
> end_lat=51.5,
> end_lng=-0.1,
> color="#00ffff",
> stroke=1.5,
> ),
> ArcDatum(
> start_lat=51.5,
> start_lng=-0.1,
> end_lat=25.3,
> end_lng=55.3,
> color="#ff6b6b",
> stroke=1.5,
> ),
> ArcDatum(
> start_lat=25.3,
> start_lng=55.3,
> end_lat=1.3,
> end_lng=103.8,
> color="#ff9f43",
> stroke=1.5,
> ),
> ArcDatum(
> start_lat=1.3,
> start_lng=103.8,
> end_lat=35.7,
> end_lng=139.7,
> color="#6bcb77",
> stroke=1.5,
> ),
> ArcDatum(
> start_lat=35.7,
> start_lng=139.7,
> end_lat=-33.9,
> end_lng=151.2,
> color="#ffd93d",
> stroke=1.5,
> ),
> ArcDatum(
> start_lat=40.7,
> start_lng=-74.0,
> end_lat=-23.5,
> end_lng=-46.6,
> color="#54a0ff",
> stroke=1.5,
> ),
> ArcDatum(
> start_lat=51.5,
> start_lng=-0.1,
> end_lat=48.9,
> end_lng=2.3,
> color="#ff6b9d",
> stroke=1.5,
> ),
> ArcDatum(
> start_lat=48.9,
> start_lng=2.3,
> end_lat=35.7,
> end_lng=139.7,
> color="#ff6b9d",
> stroke=1.5,
> ),
> ArcDatum(
> start_lat=-33.9,
> start_lng=151.2,
> end_lat=1.3,
> end_lng=103.8,
> color="#9b59b6",
> stroke=1.5,
> ),
> ]
> 
> config = GlobeConfig(
> globe=GlobeLayerConfig(
> globe_image_url="https://cdn.jsdelivr.net/npm/three-globe/example/img/earth-blue-marble.jpg"
> ),
> points=PointsLayerConfig(points_data=points),
> arcs=ArcsLayerConfig(arcs_data=arcs),
> )
> 
> pn.extension(
> "ipywidgets",
> raw_css=[
> """
> body, .bk-root, .pn-container {
> background-color: #000 !important;
> }
> """
> ],
> )
> 
> header = pn.pane.Markdown(
> """
> # Global Flight Network
> *Interactive 3D visualization of major international flight routes*
> 
> Drag to rotate | Scroll to zoom | Hover for city names
> """,
> styles={"text-align": "center", "color": "white"},
> sizing_mode="stretch_width",
> )
> 
> globe = GlobeWidget(config=config)
> globe_pane = pn.pane.IPyWidget(globe, height=800, width=800)
> 
> # Event display pane
> event_pane = pn.pane.Markdown(
> "*Hover over cities or click on arcs*",
> styles={"text-align": "center", "color": "#aaa", "font-size": "14px"},
> sizing_mode="stretch_width",
> )
> 
> def on_point_hover(current, _previous):
> if current:
> label = current.get("label", "Unknown")
> lat = current.get("lat")
> lng = current.get("lng")
> event_pane.object = (
> f" **Hovering:** {label} ({lat:.1f}, {lng:.1f})"
> if lat and lng
> else f" **Hovering:** {label}"
> )
> else:
> event_pane.object = "*Hover over cities or click on arcs*"
> 
> def on_point_click(point, _coords):
> if point:
> event_pane.object = f" **Clicked:** {point.get('label', 'Unknown')}"
> 
> def on_arc_hover(current, _previous):
> if current:
> start_lat = current.get("startLat")
> start_lng = current.get("startLng")
> end_lat = current.get("endLat")
> end_lng = current.get("endLng")
> if all(v is not None for v in [start_lat, start_lng, end_lat, end_lng]):
> event_pane.object = f" **Route:** ({start_lat:.1f}, {start_lng:.1f}) to ({end_lat:.1f}, {end_lng:.1f})"
> 
> def on_arc_click(arc, _coords):
> if arc:
> start_lat = arc.get("startLat")
> start_lng = arc.get("startLng")
> end_lat = arc.get("endLat")
> end_lng = arc.get("endLng")
> if all(v is not None for v in [start_lat, start_lng, end_lat, end_lng]):
> event_pane.object = f" **Route clicked:** ({start_lat:.1f}, {start_lng:.1f}) to ({end_lat:.1f}, {end_lng:.1f})"
> 
> globe.on_point_hover(on_point_hover)
> globe.on_point_click(on_point_click)
> globe.on_arc_hover(on_arc_hover)
> globe.on_arc_click(on_arc_click)
> 
> content = pn.Column(header, globe_pane, event_pane, align="center")
> 
> layout = pn.Row(
> pn.Spacer(sizing_mode="stretch_width"),
> content,
> pn.Spacer(sizing_mode="stretch_width"),
> styles={"background-color": "#000", "min-height": "100vh"},
> sizing_mode="stretch_width",
> )
> layout.servable()
> 
> ```

## Links

- [PyGlobeGL GitHub](https://github.com/owenlamont/pyglobegl) - check out the docs for more layer types (polygons, paths, heatmaps, rings, etc.)
