diff --git a/README.md b/README.md
index 9fae8532..fe525ea8 100644
--- a/README.md
+++ b/README.md
@@ -115,7 +115,7 @@ curl -X POST http://your-pi-ip:5000/api/v3/plugins/install \
| [Simple Clock](./plugins/clock-simple/) | Time and date display |
|
| [7-Segment Clock](./plugins/7-segment-clock/) | Retro-style 7-segment clock with customizable colors |
|
| [Google Calendar](./plugins/calendar/) | Upcoming events from Google Calendar | |
-| [Geochron World Clock](./plugins/geochron/) | World map with the real-time day/night terminator | |
+| [Geochron World Clock](./plugins/geochron/) | World map with the real-time day/night terminator |
|
### Weather (2)
diff --git a/docs/assets/geochron/clock.png b/docs/assets/geochron/clock.png
new file mode 100644
index 00000000..f075bcdf
Binary files /dev/null and b/docs/assets/geochron/clock.png differ
diff --git a/docs/assets/geochron/graticule.png b/docs/assets/geochron/graticule.png
new file mode 100644
index 00000000..8f5c58ed
Binary files /dev/null and b/docs/assets/geochron/graticule.png differ
diff --git a/docs/assets/geochron/hero.png b/docs/assets/geochron/hero.png
new file mode 100644
index 00000000..5e5a04fe
Binary files /dev/null and b/docs/assets/geochron/hero.png differ
diff --git a/docs/assets/geochron/map-centre.png b/docs/assets/geochron/map-centre.png
new file mode 100644
index 00000000..714b9c1d
Binary files /dev/null and b/docs/assets/geochron/map-centre.png differ
diff --git a/docs/assets/geochron/markers.png b/docs/assets/geochron/markers.png
new file mode 100644
index 00000000..9f482bdb
Binary files /dev/null and b/docs/assets/geochron/markers.png differ
diff --git a/docs/assets/geochron/night-brightness.png b/docs/assets/geochron/night-brightness.png
new file mode 100644
index 00000000..91be5d7c
Binary files /dev/null and b/docs/assets/geochron/night-brightness.png differ
diff --git a/docs/assets/geochron/panel-sizes.png b/docs/assets/geochron/panel-sizes.png
new file mode 100644
index 00000000..891d88fe
Binary files /dev/null and b/docs/assets/geochron/panel-sizes.png differ
diff --git a/docs/assets/geochron/seasons.png b/docs/assets/geochron/seasons.png
new file mode 100644
index 00000000..1d2b6d88
Binary files /dev/null and b/docs/assets/geochron/seasons.png differ
diff --git a/docs/assets/geochron/shots.json b/docs/assets/geochron/shots.json
new file mode 100644
index 00000000..919a8730
--- /dev/null
+++ b/docs/assets/geochron/shots.json
@@ -0,0 +1,524 @@
+{
+ "plugin": "geochron",
+ "defaults": {
+ "width": 128,
+ "height": 32,
+ "scale": 6,
+ "freeze_time": "2026-09-02T12:00:00+00:00",
+ "config": {
+ "enabled": true,
+ "timezone": "America/New_York"
+ }
+ },
+ "shots": [
+ {
+ "name": "hero",
+ "width": 256,
+ "height": 128,
+ "scale": 4
+ },
+ {
+ "name": "term-bands",
+ "config": {
+ "show_terminator_bands": true
+ },
+ "standalone": false,
+ "width": 256,
+ "height": 128,
+ "scale": 3
+ },
+ {
+ "name": "term-hard",
+ "config": {
+ "show_terminator_bands": false
+ },
+ "standalone": false,
+ "width": 256,
+ "height": 128,
+ "scale": 3
+ },
+ {
+ "name": "night-02",
+ "config": {
+ "night_brightness": 0.2
+ },
+ "standalone": false,
+ "width": 256,
+ "height": 128,
+ "scale": 3
+ },
+ {
+ "name": "night-05",
+ "config": {
+ "night_brightness": 0.5
+ },
+ "standalone": false,
+ "width": 256,
+ "height": 128,
+ "scale": 3
+ },
+ {
+ "name": "night-10",
+ "config": {
+ "night_brightness": 1.0
+ },
+ "standalone": false,
+ "width": 256,
+ "height": 128,
+ "scale": 3
+ },
+ {
+ "name": "night-00",
+ "config": {
+ "night_brightness": 0.0
+ },
+ "standalone": false,
+ "width": 256,
+ "height": 128,
+ "scale": 3
+ },
+ {
+ "name": "grid-15",
+ "config": {
+ "show_grid": true,
+ "graticule_step_deg": 15
+ },
+ "standalone": false,
+ "width": 256,
+ "height": 128,
+ "scale": 3
+ },
+ {
+ "name": "grid-30",
+ "config": {
+ "show_grid": true,
+ "graticule_step_deg": 30
+ },
+ "standalone": false,
+ "width": 256,
+ "height": 128,
+ "scale": 3
+ },
+ {
+ "name": "grid-45",
+ "config": {
+ "show_grid": true,
+ "graticule_step_deg": 45
+ },
+ "standalone": false,
+ "width": 256,
+ "height": 128,
+ "scale": 3
+ },
+ {
+ "name": "grid-90",
+ "config": {
+ "show_grid": true,
+ "graticule_step_deg": 90
+ },
+ "standalone": false,
+ "width": 256,
+ "height": 128,
+ "scale": 3
+ },
+ {
+ "name": "grid-off",
+ "config": {
+ "show_grid": false
+ },
+ "standalone": false,
+ "width": 256,
+ "height": 128,
+ "scale": 3
+ },
+ {
+ "name": "markers-all",
+ "config": {
+ "show_sun_marker": true,
+ "show_cities": true
+ },
+ "standalone": false,
+ "width": 256,
+ "height": 128,
+ "scale": 3
+ },
+ {
+ "name": "markers-sun",
+ "config": {
+ "show_sun_marker": true,
+ "show_cities": false
+ },
+ "standalone": false,
+ "width": 256,
+ "height": 128,
+ "scale": 3
+ },
+ {
+ "name": "markers-city",
+ "config": {
+ "show_sun_marker": false,
+ "show_cities": true
+ },
+ "standalone": false,
+ "width": 256,
+ "height": 128,
+ "scale": 3
+ },
+ {
+ "name": "markers-none",
+ "config": {
+ "show_sun_marker": false,
+ "show_cities": false
+ },
+ "standalone": false,
+ "width": 256,
+ "height": 128,
+ "scale": 3
+ },
+ {
+ "name": "clock-24",
+ "config": {
+ "clock_format": "24h",
+ "show_seconds": true
+ },
+ "standalone": false,
+ "width": 256,
+ "height": 128,
+ "scale": 3
+ },
+ {
+ "name": "clock-12",
+ "config": {
+ "clock_format": "12h",
+ "show_seconds": true
+ },
+ "standalone": false,
+ "width": 256,
+ "height": 128,
+ "scale": 3
+ },
+ {
+ "name": "clock-nosec",
+ "config": {
+ "clock_format": "24h",
+ "show_seconds": false
+ },
+ "standalone": false,
+ "width": 256,
+ "height": 128,
+ "scale": 3
+ },
+ {
+ "name": "clock-off",
+ "config": {
+ "show_digital_clock": false
+ },
+ "standalone": false,
+ "width": 256,
+ "height": 128,
+ "scale": 3
+ },
+ {
+ "name": "season-mar",
+ "freeze_time": "2026-03-20T12:00:00+00:00",
+ "standalone": false,
+ "width": 256,
+ "height": 128,
+ "scale": 3
+ },
+ {
+ "name": "season-jun",
+ "freeze_time": "2026-06-21T12:00:00+00:00",
+ "standalone": false,
+ "width": 256,
+ "height": 128,
+ "scale": 3
+ },
+ {
+ "name": "season-sep",
+ "freeze_time": "2026-09-22T12:00:00+00:00",
+ "standalone": false,
+ "width": 256,
+ "height": 128,
+ "scale": 3
+ },
+ {
+ "name": "season-dec",
+ "freeze_time": "2026-12-21T12:00:00+00:00",
+ "standalone": false,
+ "width": 256,
+ "height": 128,
+ "scale": 3
+ },
+ {
+ "name": "p-64x32",
+ "width": 64,
+ "height": 32,
+ "scale": 8,
+ "standalone": false
+ },
+ {
+ "name": "p-128x32",
+ "width": 128,
+ "height": 32,
+ "scale": 8,
+ "standalone": false
+ },
+ {
+ "name": "p-128x64",
+ "width": 128,
+ "height": 64,
+ "scale": 6,
+ "standalone": false
+ },
+ {
+ "name": "p-256x128",
+ "width": 256,
+ "height": 128,
+ "scale": 3,
+ "standalone": false
+ },
+ {
+ "name": "centre-auto",
+ "width": 64,
+ "height": 64,
+ "scale": 8,
+ "standalone": false
+ },
+ {
+ "name": "centre-0",
+ "config": {
+ "map_center_longitude": 0
+ },
+ "width": 64,
+ "height": 64,
+ "scale": 8,
+ "standalone": false
+ },
+ {
+ "name": "centre-neg",
+ "config": {
+ "map_center_longitude": -100
+ },
+ "width": 64,
+ "height": 64,
+ "scale": 8,
+ "standalone": false
+ },
+ {
+ "name": "centre-pos",
+ "config": {
+ "map_center_longitude": 140
+ },
+ "width": 64,
+ "height": 64,
+ "scale": 8,
+ "standalone": false
+ }
+ ],
+ "composites": [
+ {
+ "name": "terminator",
+ "columns": 1,
+ "cells": [
+ {
+ "shot": "term-bands",
+ "label": "show_terminator_bands: true",
+ "sublabel": "the default; civil, nautical and astronomical twilight blend into night"
+ },
+ {
+ "shot": "term-hard",
+ "label": "show_terminator_bands: false",
+ "sublabel": "a hard day/night line"
+ }
+ ]
+ },
+ {
+ "name": "night-brightness",
+ "columns": 2,
+ "cells": [
+ {
+ "shot": "night-00",
+ "label": "night_brightness: 0.0",
+ "sublabel": "the night side goes black"
+ },
+ {
+ "shot": "night-02",
+ "label": "night_brightness: 0.2",
+ "sublabel": "the default"
+ },
+ {
+ "shot": "night-05",
+ "label": "night_brightness: 0.5",
+ "sublabel": "land still readable at night"
+ },
+ {
+ "shot": "night-10",
+ "label": "night_brightness: 1.0",
+ "sublabel": "no darkening at all; only the tint remains"
+ }
+ ]
+ },
+ {
+ "name": "graticule",
+ "columns": 2,
+ "cells": [
+ {
+ "shot": "grid-15",
+ "label": "graticule_step_deg: 15",
+ "sublabel": "one line per hour of longitude"
+ },
+ {
+ "shot": "grid-30",
+ "label": "graticule_step_deg: 30",
+ "sublabel": "the default"
+ },
+ {
+ "shot": "grid-45",
+ "label": "graticule_step_deg: 45",
+ "sublabel": ""
+ },
+ {
+ "shot": "grid-90",
+ "label": "graticule_step_deg: 90",
+ "sublabel": "equator and prime meridian only"
+ },
+ {
+ "shot": "grid-off",
+ "label": "show_grid: false",
+ "sublabel": "no graticule"
+ }
+ ]
+ },
+ {
+ "name": "markers",
+ "columns": 2,
+ "cells": [
+ {
+ "shot": "markers-all",
+ "label": "Both (the default)",
+ "sublabel": "subsolar point in yellow, cities in red"
+ },
+ {
+ "shot": "markers-sun",
+ "label": "show_cities: false",
+ "sublabel": "just the subsolar point"
+ },
+ {
+ "shot": "markers-city",
+ "label": "show_sun_marker: false",
+ "sublabel": "just the cities"
+ },
+ {
+ "shot": "markers-none",
+ "label": "Neither",
+ "sublabel": "the bare map"
+ }
+ ]
+ },
+ {
+ "name": "clock",
+ "columns": 2,
+ "cells": [
+ {
+ "shot": "clock-24",
+ "label": "24h with seconds",
+ "sublabel": "the default"
+ },
+ {
+ "shot": "clock-12",
+ "label": "clock_format: 12h",
+ "sublabel": ""
+ },
+ {
+ "shot": "clock-nosec",
+ "label": "show_seconds: false",
+ "sublabel": "a shorter readout"
+ },
+ {
+ "shot": "clock-off",
+ "label": "show_digital_clock: false",
+ "sublabel": "map only"
+ }
+ ]
+ },
+ {
+ "name": "seasons",
+ "columns": 2,
+ "cells": [
+ {
+ "shot": "season-mar",
+ "label": "20 March",
+ "sublabel": "equinox: the terminator runs pole to pole"
+ },
+ {
+ "shot": "season-jun",
+ "label": "21 June",
+ "sublabel": "northern solstice: the Arctic is in full daylight"
+ },
+ {
+ "shot": "season-sep",
+ "label": "22 September",
+ "sublabel": "equinox again, tilted the other way through the year"
+ },
+ {
+ "shot": "season-dec",
+ "label": "21 December",
+ "sublabel": "southern solstice: the Antarctic never sets"
+ }
+ ]
+ },
+ {
+ "name": "panel-sizes",
+ "columns": 1,
+ "cells": [
+ {
+ "shot": "p-64x32",
+ "label": "64 x 32",
+ "sublabel": "a slice of the map, centred on your timezone"
+ },
+ {
+ "shot": "p-128x32",
+ "label": "128 x 32",
+ "sublabel": "the common two-panel chain"
+ },
+ {
+ "shot": "p-128x64",
+ "label": "128 x 64",
+ "sublabel": "most of the globe"
+ },
+ {
+ "shot": "p-256x128",
+ "label": "256 x 128",
+ "sublabel": "the full 360 degrees, which is what this plugin wants"
+ }
+ ]
+ },
+ {
+ "name": "map-centre",
+ "columns": 2,
+ "cells": [
+ {
+ "shot": "centre-auto",
+ "label": "auto (null)",
+ "sublabel": "derived from your timezone"
+ },
+ {
+ "shot": "centre-0",
+ "label": "map_center_longitude: 0",
+ "sublabel": "Greenwich"
+ },
+ {
+ "shot": "centre-neg",
+ "label": "-100",
+ "sublabel": "North America"
+ },
+ {
+ "shot": "centre-pos",
+ "label": "140",
+ "sublabel": "East Asia and Australia"
+ }
+ ]
+ }
+ ]
+}
diff --git a/docs/assets/geochron/terminator.png b/docs/assets/geochron/terminator.png
new file mode 100644
index 00000000..c290a692
Binary files /dev/null and b/docs/assets/geochron/terminator.png differ
diff --git a/plugins.json b/plugins.json
index f900d57c..9eecd2a9 100644
--- a/plugins.json
+++ b/plugins.json
@@ -263,10 +263,10 @@
"plugin_path": "plugins/geochron",
"stars": 0,
"downloads": 0,
- "last_updated": "2026-07-17",
+ "last_updated": "2026-09-02",
"verified": true,
"screenshot": "",
- "latest_version": "1.0.3",
+ "latest_version": "1.0.4",
"icon": "fa-globe"
},
{
diff --git a/plugins/geochron/README.md b/plugins/geochron/README.md
index b22e7dee..3e4157a2 100644
--- a/plugins/geochron/README.md
+++ b/plugins/geochron/README.md
@@ -1,89 +1,335 @@
# Geochron World Clock
-
+A real-time Geochron-style world map: an equirectangular map of the Earth with
+a live day/night terminator, smooth twilight bands, the subsolar point,
+configurable city markers, and a digital clock — scaled to fit any panel size
+or shape.
-A real-time "Geochron"-style world map: a high-fidelity equirectangular map
-of the Earth with a live day/night terminator, smooth twilight bands, the
-subsolar point, configurable city markers, and a digital clock - scaled to
-fit any LED matrix panel size or shape.
+
+
+*Every image in this README is real plugin output, rendered at the true panel
+size against a frozen clock and then scaled up so the pixels stay pixels. The
+terminator positions are what the plugin actually computes for the dates
+shown.*
**Data source:** [Natural Earth](https://www.naturalearthdata.com/) 110m
-Admin-0 Countries (public domain), vendored locally - no network access
-required.
+Admin-0 Countries (public domain), vendored locally — **no network access
+required**.
---
-## How it works
+## Table of Contents
-- The sun's subsolar point (the location on Earth where the sun is directly
- overhead) is computed from a NOAA simplified solar position algorithm,
- accurate to a fraction of a degree.
-- A 720x360 equirectangular base map is rasterized once at startup from the
- vendored country outlines, then cropped/resized per panel size for crisp,
- high-resolution output regardless of matrix dimensions.
-- Every `update_interval` seconds, the night side of the map is darkened and
- tinted, with smooth civil/nautical/astronomical twilight bands across the
- terminator (or a hard day/night line if bands are disabled).
-- The digital clock ticks every frame for smooth seconds, independent of the
- map update cadence.
+1. [How It Works](#how-it-works)
+2. [Installation](#installation)
+3. [Layout Modes](#layout-modes)
+4. [Configuration Reference](#configuration-reference)
+ - [The terminator](#the-terminator)
+ - [The graticule](#the-graticule)
+ - [Markers](#markers)
+ - [The clock](#the-clock)
+ - [Cities](#cities)
+ - [Map centring](#map-centring)
+ - [Colours](#colours)
+5. [Through the Year](#through-the-year)
+6. [Troubleshooting](#troubleshooting)
+7. [Development](#development)
+8. [Support](#support)
---
-## Layout modes
+## How It Works
+
+- The **subsolar point** — where the sun is directly overhead — is computed
+ from a NOAA simplified solar position algorithm, accurate to a fraction of a
+ degree.
+- A **720×360 equirectangular base map** is rasterised once at startup from the
+ vendored country outlines, then cropped and resized per panel size, so output
+ stays crisp whatever the matrix dimensions.
+- Every `update_interval` seconds the night side is darkened and tinted, with
+ smooth civil, nautical and astronomical twilight bands across the terminator
+ — or a hard line if bands are off.
+- The **digital clock ticks every frame** for smooth seconds, independent of
+ the map's update cadence. That is why `update_interval` can be generous
+ without the clock stuttering.
+
+All computation is `numpy` over a fixed lat/lon grid, cheap enough to run on
+Pi-class hardware.
+
+---
-The map and overlays adapt automatically based on panel aspect ratio:
+## Installation
+
+**From the Plugin Store (recommended).** Open the LEDMatrix web interface at
+`http://:5000`, go to **Plugin Manager**, find **Geochron World
+Clock** in the **Plugin Store** section, and click **Install**.
+
+**Manually.** Copy this directory into your LEDMatrix `plugin-repos/` and
+restart the display service.
+
+`enabled` defaults to **`false`**. There is nothing else to configure — the map
+data ships with the plugin and no API key or account is involved.
+
+---
+
+## Layout Modes
+
+The map and overlays adapt to the panel's **aspect ratio**, not its absolute
+size:
| Aspect ratio | Mode | Layout |
-|---|---|---|
-| >= 3.0 (e.g. 128x32, 256x32) | Wide sidebar | Map + a sidebar with UTC time/date, local time, and subsolar coordinates |
-| 1.5 - 3.0 (e.g. 64x32, 128x64) | Near bleed | Full-bleed map with a small corner time readout |
-| < 1.5 (e.g. 64x64, 128x96) | Square/tall | Full-bleed map cropped to a configurable longitude band, with a corner readout |
+|--------------|------|--------|
+| ≥ 3.0 (128×32, 256×32) | Wide sidebar | Map plus a sidebar with UTC time and date, local time, and subsolar coordinates |
+| 1.5 – 3.0 (64×32, 128×64) | Near bleed | Full-bleed map with a small corner readout |
+| < 1.5 (64×64, 128×96) | Square / tall | Full-bleed map cropped to a longitude band, with a corner readout |
+
+
+
+This plugin rewards width more than most: a 256×128 panel shows the full 360°
+at a glance, while a 64-wide panel can only show a slice — which is why
+[map centring](#map-centring) exists.
---
-## Configuration
-
-| Option | Default | Description |
-|--------|---------|-------------|
-| `display_duration` | `20` | Seconds to show before rotating to the next plugin |
-| `update_interval` | `45` | How often (seconds) to recompute the sun position and re-render the map |
-| `timezone` | `null` | IANA timezone for the local time readout and default map centering. `null` inherits the global LEDMatrix timezone |
-| `map_center_longitude` | `null` | Longitude to center on for square/tall panels. `null` auto-derives from the local timezone's UTC offset |
-| `show_terminator_bands` | `true` | Smooth civil/nautical/astronomical twilight gradient vs. a hard day/night line |
-| `night_brightness` | `0.20` | Brightness multiplier for the night side of the map |
-| `show_grid` | `true` | Draw a lat/lon graticule |
-| `graticule_step_deg` | `30` | Graticule line spacing in degrees (15/30/45/90) |
-| `show_sun_marker` | `true` | Draw a marker at the subsolar point |
-| `show_cities` | `true` | Draw configured city markers |
-| `cities` | 8 classic cities | Up to 8 `{name, lat, lon, timezone}` entries |
-| `show_digital_clock` | `true` | Show the digital time readout |
-| `clock_format` | `24h` | `12h` or `24h` |
-| `show_seconds` | `true` | Show seconds in the clock readout |
-| `colors` | see `config_schema.json` | Ocean, land, coastline, night tint, sun marker, city marker, grid, and text colors |
+## Configuration Reference
+
+| Option | Type | Default | What it does |
+|--------|------|---------|--------------|
+| `enabled` | boolean | `false` | Whether the plugin runs at all |
+| `display_duration` | number | `20` | Seconds on screen before the rotation moves on |
+| `update_interval` | integer | `45` | Seconds between recomputing the sun and re-rendering the map |
+| `timezone` | string / null | `null` | IANA zone for the local readout and default centring; `null` inherits the global setting |
+| `map_center_longitude` | number / null | `null` | Longitude to centre on for square/tall panels |
+| `show_terminator_bands` | boolean | `true` | Twilight gradient, or a hard day/night line |
+| `night_brightness` | number | `0.2` | Brightness multiplier for the night side |
+| `show_grid` | boolean | `true` | Draw the lat/lon graticule |
+| `graticule_step_deg` | integer | `30` | Graticule spacing: `15`, `30`, `45` or `90` |
+| `show_sun_marker` | boolean | `true` | Marker at the subsolar point |
+| `show_cities` | boolean | `true` | Markers for the configured cities |
+| `cities` | array | 8 cities | Up to 8 `{name, lat, lon, timezone}` entries |
+| `show_digital_clock` | boolean | `true` | The digital time readout |
+| `clock_format` | string | `24h` | `12h` or `24h` |
+| `show_seconds` | boolean | `true` | Seconds in the readout |
+| `colors.*` | array | see below | Nine RGB colours for map and text elements |
+
+The terminator drifts about a quarter of a degree a minute, so
+`update_interval: 45` is already finer than the panel can show. Raising it to
+several minutes costs nothing visible and saves CPU; the clock is unaffected
+either way.
+
+### The terminator
+
+
+
+With `show_terminator_bands` on — the default — the night side fades in through
+civil, nautical and astronomical twilight, which is what makes the boundary
+look like dusk rather than a cut. Turning it off draws a hard line, which is
+sharper on a small panel where a gradient has only a few pixels to work in.
+
+`night_brightness` controls how dark the night side goes:
+
+
+
+At `0.0` the night side goes black and only the terminator reads. The default
+`0.2` keeps continents faintly visible. At `1.0` there is no darkening at all
+and only the tint remains, which makes the day/night boundary nearly invisible
+— useful only if you want the map for its own sake.
+
+### The graticule
+
+
+
+`graticule_step_deg: 15` gives one line per hour of longitude, which is
+handsome on a large panel and noise on a small one. `90` leaves just the
+equator and the prime meridian. All four values render distinctly.
+
+### Markers
+
+
+
+The **subsolar point** is the yellow marker — the single place on Earth where
+the sun is directly overhead at that instant. It tracks west at roughly 15° an
+hour and north/south with the seasons, which is what the
+[seasonal comparison](#through-the-year) below shows.
+
+**City markers** are red dots at each configured city. Labels and local times
+appear when the panel has room for them.
+
+### The clock
+
+
+
+The readout shows UTC time and date, and on a wide panel the local time and the
+subsolar coordinates as well. It redraws every frame regardless of
+`update_interval`, so seconds tick smoothly.
+
+### Cities
+
+`cities` takes up to eight entries:
+
+```json
+{
+ "cities": [
+ { "name": "New York", "lat": 40.71, "lon": -74.01, "timezone": "America/New_York" },
+ { "name": "Tokyo", "lat": 35.68, "lon": 139.65, "timezone": "Asia/Tokyo" }
+ ]
+}
+```
+
+| Key | What it does |
+|-----|--------------|
+| `name` | Label, shown when there is room |
+| `lat` / `lon` | Decimal degrees; negative is south and west |
+| `timezone` | IANA zone, used for that city's local time |
+
+The defaults are eight well-spread cities, chosen to span the map rather than
+for any other reason. Replace them with your own — these are here so you can
+copy the exact timezone strings:
+
+| City | Latitude | Longitude | Timezone |
+|------|----------|-----------|----------|
+| New York | `40.71` | `-74.01` | `America/New_York` |
+| Los Angeles | `34.05` | `-118.24` | `America/Los_Angeles` |
+| Rio de Janeiro | `-22.91` | `-43.17` | `America/Sao_Paulo` |
+| London | `51.51` | `-0.13` | `Europe/London` |
+| Cairo | `30.04` | `31.24` | `Africa/Cairo` |
+| Moscow | `55.75` | `37.62` | `Europe/Moscow` |
+| Tokyo | `35.68` | `139.65` | `Asia/Tokyo` |
+| Sydney | `-33.87` | `151.21` | `Australia/Sydney` |
+
+### Map centring
+
+On a square or tall panel the map cannot show all 360°, so it is cropped to a
+band. `map_center_longitude` picks the centre of that band:
+
+
+
+Left at `null` it is derived from your timezone's UTC offset, which puts your
+part of the world in the middle — usually what you want. Set it explicitly to
+watch somewhere else. It has no effect on wide panels, which show everything
+anyway.
+
+### Colours
+
+Nine settings under `colors`, each an `[R, G, B]` array:
+
+| Key | Default | Colours |
+|-----|---------|---------|
+| `ocean_color` | `[10, 35, 90]` | Water |
+| `land_color` | `[40, 110, 50]` | Land masses |
+| `coastline_color` | `[90, 160, 100]` | Coastlines and country borders |
+| `night_tint_color` | `[10, 10, 40]` | What the night side is tinted toward |
+| `sun_marker_color` | `[255, 220, 0]` | The subsolar point |
+| `city_marker_color` | `[255, 60, 60]` | City dots |
+| `grid_color` | `[70, 70, 70]` | The graticule |
+| `text_primary_color` | `[255, 255, 255]` | Clock and headings |
+| `text_secondary_color` | `[180, 180, 180]` | Dates and labels |
+
+Keep `coastline_color` lighter than `land_color` — the coastline is what gives
+the continents their shape at small sizes, and losing the contrast turns the
+map into green blobs.
---
-## Default cities
+## Through the Year
+
+The terminator's shape is the plugin's most visible output, and it is not
+decorative — it is computed, and it changes with the season:
-| City | Timezone |
-|------|----------|
-| New York | America/New_York |
-| Los Angeles | America/Los_Angeles |
-| Rio de Janeiro | America/Sao_Paulo |
-| London | Europe/London |
-| Cairo | Africa/Cairo |
-| Moscow | Europe/Moscow |
-| Tokyo | Asia/Tokyo |
-| Sydney | Australia/Sydney |
+
-Edit, remove, or add to this list (up to 8 entries) in the plugin
-configuration.
+At the **equinoxes** the terminator runs nearly pole to pole and day and night
+are equal everywhere. At the **June solstice** the subsolar point sits over the
+Tropic of Cancer and the Arctic never gets dark; at the **December solstice**
+it is the Antarctic's turn. All four are rendered from the same code that runs
+on your panel, at 12:00 UTC on the real dates.
---
-## Notes
+## Troubleshooting
+
+**Nothing appears.**
+`enabled` defaults to `false`.
+
+**The local time is wrong.**
+`timezone` is `null` by default and inherits the global LEDMatrix setting.
+Check that first, then set it here if you want this plugin to differ.
+
+**The map is centred on the wrong part of the world.**
+On a square or tall panel that is `map_center_longitude` — see
+[Map centring](#map-centring). On a wide panel the whole map is shown and there
+is nothing to centre.
+
+**The day/night boundary is hard to see.**
+`night_brightness` may be too high. The default is `0.2`; at `1.0` there is
+effectively no darkening.
+
+**City labels do not appear.**
+They are drawn only when the panel has room. The dots are always drawn — on a
+small panel that is all you get.
+
+**The map looks like green blobs.**
+Coastlines are probably too close in colour to the land. Keep
+`coastline_color` clearly lighter than `land_color`.
+
+**The seconds stutter.**
+They should not — the clock redraws every frame independently of
+`update_interval`. If they do, the panel's overall frame rate is the
+constraint, not this setting.
+
+---
+
+## Development
+
+### Project structure
+
+```text
+geochron/
+├── manifest.json # Plugin metadata and version history
+├── manager.py # GeochronPlugin — config, layout modes, overlays
+├── geochron_renderer.py # The drawing, shared with render_preview.py
+├── solar.py # Subsolar point and twilight bands
+├── worldmap.py # Rasterises the vendored country outlines
+├── data/ # Natural Earth 110m countries (public domain)
+├── render_preview.py # Standalone preview generator
+├── config_schema.json # Settings schema; source of truth for defaults
+└── test/
+```
+
+`render_preview.py` imports `geochron_renderer` — the same module `manager.py`
+draws with — so its previews cannot disagree with the plugin. That is worth
+copying if you add a preview generator elsewhere: share the renderer rather
+than keeping a second copy of the drawing code.
+
+### Regenerating the images in this README
+
+```bash
+python scripts/render_docs_assets.py --plugin geochron
+```
+
+`--check` verifies the committed images still match. The clock is frozen in the
+shot list, which is what pins the terminator — without that every image would
+change by the minute.
+
+---
+
+## Support
+
+- YouTube:
+- Instagram:
+- Discord:
+- Sponsor: [GitHub Sponsors](https://github.com/sponsors/ChuckBuilds) ·
+ [Buy Me a Coffee](https://buymeacoffee.com/chuckbuilds) ·
+ [Ko-fi](https://ko-fi.com/chuckbuilds/)
-- Map data is vendored at `data/world-countries.geojson` (simplified Natural
- Earth 110m countries, public domain) - the plugin makes no network calls.
-- All computation is done with `numpy` over a fixed lat/lon grid, so it's
- cheap enough to run on Pi-class hardware every `update_interval` seconds.
+Released under the GNU General Public License v3.0 — see [LICENSE](LICENSE).
diff --git a/plugins/geochron/manifest.json b/plugins/geochron/manifest.json
index 55e22752..a7ba75df 100644
--- a/plugins/geochron/manifest.json
+++ b/plugins/geochron/manifest.json
@@ -1,7 +1,7 @@
{
"id": "geochron",
"name": "Geochron World Clock",
- "version": "1.0.3",
+ "version": "1.0.4",
"author": "ChuckBuilds",
"description": "High-fidelity world map clock showing the real-time day/night terminator with civil/nautical/astronomical twilight bands, the subsolar point, configurable city markers, and a digital UTC/local clock - a classic Geochron, reimagined for LED matrix panels of any size.",
"entry_point": "manager.py",
@@ -24,6 +24,12 @@
">=2.0.0"
],
"versions": [
+ {
+ "version": "1.0.4",
+ "released": "2026-09-02",
+ "ledmatrix_min_version": "2.0.0",
+ "notes": "Documentation only, no behaviour change. Adds real rendered screenshots for every setting that has a visible effect and documents all 24 of them. The hero image was an uploaded GitHub attachment on an external URL; it is now a committed render that regenerates from the plugin and is verified by --check. Adds a seasonal comparison showing the terminator at both equinoxes and both solstices, rendered from the real solar code at 12:00 UTC on those dates, and a map-centring comparison for square and tall panels. Both enum settings were hash-checked: all four graticule_step_deg values and both clock_format values render distinctly. The existing content -- how it works, the aspect-ratio layout table, the default cities and the vendored-data notes -- is kept, with the cities table regaining the latitude and longitude alongside the timezone strings."
+ },
{
"version": "1.0.3",
"released": "2026-08-14",
@@ -54,7 +60,7 @@
"license": "GPL-3.0",
"homepage": "https://github.com/ChuckBuilds/ledmatrix-plugins/tree/main/plugins/geochron",
"config_schema": "config_schema.json",
- "last_updated": "2026-07-17",
+ "last_updated": "2026-09-02",
"stars": 0,
"downloads": 0,
"verified": true,