尚未偵測到更新。GitHub 發布版本後會顯示在這裡。
個人 GitHub 帳號下公開儲存庫的擁有者可以登入並驗證所有權,自行管理介紹,無需管理員核准。
登入並產生驗證檔案,在 30 分鐘內將其提交至此儲存庫的預設分支。其他分支或分叉儲存庫中的檔案無效。然後在下方檢查檔案,即可無需管理員核准直接管理專案。請保留檔案以維持管理權限。
如需登入,將前往登入頁面。登入後請回到此處,再次點擊所選的驗證按鈕。
在專案資料夾中執行 /nomorevibe verify,即可驗證所有權並自行管理介紹。
一分鐘完成驗證Meridian · Track your Travels: an interactive 3D globe to mark the countries, states and cities you've visited, log flights as trips, look back on each year with a time-lapse, keep a wishlist, compare with a friend, and play 12 geography games. Installable, works offline. React, TypeScript, three.js.
翻譯準備中
取自儲存庫 README
正在載入完整 README。 以下為已儲存的摘錄。
An interactive 3D globe for keeping track of where you've been. Mark the countries, states and cities you've visited and when, log your flights and see them grouped into trips, look back on each year or replay them all as a time-lapse, keep a wishlist, compare your travels with a friend's, and test your geography with twelve games.
Try it at https://simongsk.github.io/meridian-track-your-travels/. It runs in any browser, on a phone too, can be installed as an app, and works offline. Everything you add stays in your browser.檢視完整 README ↗An interactive 3D globe for keeping track of where you've been. Mark the countries, states and cities you've visited and when, put your visits and flights together into trips, look back on each year, replay your trips flight by flight, keep a wishlist, compare your travels with a friend's, and test your geography with fourteen games.
Try it at https://simongsk.github.io/meridian-track-your-travels/. It runs in any browser, on a phone too, can be installed as an app, and works offline. Everything you add stays in your browser.
Spin the globe, hover a country to see its name and flag, and click it to fly there and see its capital, inhabitants and area, the time there, what its money is worth in yours, and the languages spoken.
The design (navy and amber, after a mock-up made in Lovable) has a top bar with the four tabs, where the globe is looking and how many places you've visited. The selected country shows on the left: its ISO code, capital, inhabitants and area, its local time, currency and languages, the cities you've visited there, its states, and a button to put it in your visited atlas. The open tab's cards are on the right. On phones the tabs move to the bottom and panels open as sheets (Explore's two buttons stay in the top corner), with the tab's name and a close button above the cards, which scroll under them. A sheet, a tab's or a country's, opens midway and can be swiped: up, it goes all the way up, from just under the top bar, to see more; down from there, it goes back to midway (or closes, swiped far); down from midway, it closes. Swipe from its top (the little bar), or from anywhere in it: down once what's in it is scrolled to the top, and up while it's midway (all the way up, swiping up scrolls). It follows the finger, and goes where it's let go a quarter of the way, or flicked; otherwise it springs back.
Explore: two round buttons in the corner, on phones too, so the globe has the room. There's always a tab open: the app opens in Explore, and closing another panel (×, Escape, or swiping its sheet down) goes back to it. The magnifying glass opens a search of the whole atlas, countries by any name and cities. The layers button opens the designs and the layers.
Designs: switch the globe between Midnight (the default), Classic, Vintage, Political (neighbors always in different colors), Night, Minimal and Realistic. Realistic shows the Earth as seen from space, with its oceans, forests, deserts, ice and snow, the sea shining in the light, and mountains in relief (zoom in on the Himalayas or the Andes). With Day and night on, its night side is dark as from space, the lights bright on it, and your places glow faintly in their colors through the dark, so they still show there. Only your places (and whatever else is colored, like the wishlist or a game's answers) are painted over it, see-through, so the land still shows under them. Each design is a tiny globe in its colors, all in a row, with the one chosen named under them. Under them are the layers to show or hide: city pins, visited countries and their heat map by visits, visited states, flights (and under them, replaying your trips in the screensaver), future flights (those booked for trips to come, dashed), future visits (the places of trips to come, tinted and outlined in dashes), the wishlist, day and night, and rings around small islands. Each has a dot in the color it is on the globe in the design chosen, and they're listed by color, then by name.
Day and night (off until switched on) darkens the side of the globe where the sun has set, as it is right now, fading through twilight down to 18° below the horizon, and lights it with the Earth's lights at night as seen from space, from NASA's picture of them, in every design: whole regions as they glow, not just their cities (the US East Coast, the Nile, dark central Africa). Should the picture not load (offline before it was kept), each city is a dot of light instead, bigger for more people. City lights shows under it while it's on, to have night without the lights. It moves on every minute, and is hidden during games. Where the sun is overhead comes from the Astronomical Almanac's low-precision formulas (src/globe/sun.ts), good to about 0.01°.
Visited: three symbols switch between your countries (a flag), trips (a plane) and years (a calendar); pointing at one shows its name. Each has a small box of figures. Keep track of where you've been, out of the world's 197 countries, with the count and percentage for each continent. Under the share of the world's countries is the share of its people your places are home to, and of its land they cover ("Home to 24% of its people, on 36% of its land"): people from the World Bank, and land as big as each place is on the map, which measures every place alike (the World Bank counts only Greenland's land free of ice). Each place counts for itself, so Greenland's people and land are Greenland's, not Denmark's. "✨ Your atlas as a poster" makes all of it a card to keep or share, as a year wrapped is (under Years, below): how many of the world's countries, a flat map of the world with every place been to and every route flown, how many territories, continents and cities, the share of the world's people and land, the flags of every place by continent (as big as they all fit, down to a size where the rest are "+N"), and your flights, trips and the first year you have a date for. or , and Escape or ✕ puts it away. Territories are counted separately, and also count for the country they belong to: marking just Greenland counts Denmark as a country been to, listed under Europe as "In Greenland" (with nothing to remove, as it isn't marked itself). The globe colors only what you've marked. Click a continent's bar to see its places over everything else: those you've visited first (with their visits, states and cities), then its countries not yet; click one to see it, or close the list with ×, Escape or a click beside it. A country's territories are listed under it, in its continent, whether you've been to the country or not: the Faroe Islands and Greenland under Denmark in Europe, Puerto Rico under the United States. Those you haven't visited are quieter, with a ✓ to mark them. Places that are no country's (Antarctica, Western Sahara, the Siachen Glacier) are listed on their own under their continent, so Antarctica has a bar too, counting its places. Search to add places (old names like "Swaziland" work too), or click a country and press "Add to visited atlas". They're colored on the globe.
The globe spins on its own until you touch it, and again once it's been left alone for 30 seconds. Tiny countries and islands get a ring marker, and clicks just beside a small island still count, also with the rings hidden. Zoomed in, tiny places reach 3.5 km around them, so pointing near Vatican City finds it (at this map's scale it's drawn 1.6 km from where it is). Only a pin's head answers to the pointer, so what's under its stem stays clickable. City pins fade out as they near the edge of the globe.
Meridian is online at https://simongsk.github.io/meridian-track-your-travels/, so it opens in any browser, on a phone too, without installing anything. Every push to main builds it and puts the new version there with GitHub Pages (.github/workflows/deploy.yml, about a minute). It's served over https://, so it can be installed as an app and works offline (below), and a friend opening it is at the same address as you, so "Copy my link" works between you.
Your places are kept by the browser for each address, so the online app starts empty even if you've used npm run dev. To bring them over, Download backup under More at http://localhost:5173 and Restore from a backup… online (see Backup).
To set it up the first time, the repository has to be public (GitHub Pages is free only for public repositories), and in its Settings › Pages, Source set to GitHub Actions (skip the suggested workflows, like Jekyll or Static HTML: the repository has its own). Then run the Deploy workflow from the Actions tab, or push to main.
The globe can be your Mac's screensaver, spinning with your places on it. With ?screensaver in the address the app shows only the globe, and the pointer doesn't stop it. It looks at the globe from just north of the equator, so as it turns you see Europe and Canada but also Australia and New Zealand, and its pins stay until closer to the edge. A screensaver keeps its own storage, so the address carries your places, flights, trips, wishlist, design and layers in its #places=… part. Trips to come show as on the globe, with Future visits and Future flights switched on.
With Replay timeline on (a switch at the top of More › Screensaver, on unless switched off, and only with the Flights layer shown), the screensaver replays your trips every so often: ten seconds after it starts, then a minute and a half after each one ends. It's the replay from the Trips list, without the bar, and without moving the globe: it spins on as it was, while every trip been on comes back in its turn, oldest first, each flight flown by its plane, drawing its line behind it, and a country colored as the flight lands in it, or in its turn when it's reached without one, those of the trip it's on standing out. Then everything shows again until the next. The preview in the app replays too.
npm run build:screensaver. It builds the app into one self-contained file, screensaver/index.html, that opens from disk without a server, and copies it to /Users/Shared/Meridian/ (screensavers can't read Documents, Desktop or Downloads).brew install --cask webviewscreensaver. (Its README adds --no-quarantine, but current Homebrew no longer has that option; macOS asks you to allow the screensaver instead, below.)The screensaver doesn't update by itself. After changing your places, design or layers, copy the address again and paste it in Options; after changing the app, run npm run build:screensaver again. An older screensaver doesn't know what's been added to the app since, so it leaves out what it can't show (a screensaver from before trips shows none of them), and one built before October 4th sets every layer back on when the address has layers it doesn't know.
"Preview", next to the address in the Screensaver card, shows the screensaver right there in the app, to have a look: everything but the spinning globe steps aside, and the globe turns to the screensaver's view. An "Exit preview" button shows in the corner as it opens, and again when you move the mouse (with the pointer) or tap the screen on a phone; it and Escape bring everything back, with the Screensaver card still open. The real screensaver has neither.
Visited places, when you went and your notes, states, cities, flights and trip names, the wishlist, visits planned, a friend you compare with, best scores and times, daily challenges, the design, the settings and your currency are saved in your browser (localStorage). Nothing is sent anywhere, so clearing the browser's site data, or moving to another browser or computer, would leave them behind. Under More, Download backup saves all of it to a file (meridian-backup-2026-10-04.json, readable JSON), and shows when you last did. Restore from a backup… reads one, says what it holds and when it was made, and only replaces what's in this browser when you confirm. Every part of the file is checked the way the app checks it when loading, so a damaged or foreign file is refused as a whole rather than half restored.
Removing something by mistake is easy to take back: removing a country, a visit, a city, a flight, a place on the wishlist or a friend shows a note at the bottom ("Removed Japan · Undo") for eight seconds. Undo, or Cmd/Ctrl+Z when you're not typing, puts it back as it was: a visit with its note, a flight where it was among the others (so its trip stays the same). Only the last removal can be undone.
Opening the app never writes over what's saved: something is saved only when you change it. So if the app finds data it can't read (saved by a newer version, or damaged), it leaves it as it is. If you then change that part, the new data is saved and the old is kept beside it, under the same name with .unreadable added.
Meridian can be installed as an app, which opens in its own window from the dock or home screen, with its globe icon:
It's called just Meridian. An app installed under an earlier name (it was "Meridian · Countries of the World", then "Meridian · Track your Travels") can keep that name in its window and menus until the browser updates it, which Chrome does now and then when it opens the app; to have it at once, uninstall the app and install it again. Your places are kept by the browser for the address, not by the app, so they stay, as long as you don't tick "Also clear data" when uninstalling (download a backup first, to be safe).
After the first visit it works offline, installed or not: the globe, your places and flights, the cities and airports, and every game. The build writes a service worker (sw.js, by vite.offline.ts) that keeps a copy of every file of the app, with a version made from their contents. Online, the page comes from the network, so a new build is noticed. Once the new version has been downloaded, in the background, a note at the bottom says "A new version is ready": Reload switches to it at once, and otherwise it takes over the next time the app is opened. Either way the old copy is cleared. While the app stays open, it looks for a new version every hour and whenever you come back to it. The service worker runs only in the production build served over the web (not npm run dev, nor the screensaver). Browsers allow one only on https:// or localhost.
Built with React, TypeScript and Vite, using react-globe.gl (three.js) for the globe, world-atlas (Natural Earth 1:50m) for country shapes and flag-icons for flags. The fonts are Fraunces, Inter and JetBrains Mono (SIL Open Font License), from Fontsource. Flags and fonts are bundled, so no requests go to third parties, but for the day's exchange rates (below).
npm install
npm run dev
Then open http://localhost:5173.
To see the app with someone's travels already in it, run npm run dev:demo and open http://localhost:5174. It has 42 places with visits over ten years (some with notes, and several visited again and again for the heat map), states, cities, trips made from its flights (three of them named), a wishlist, a trip to India coming up (with its flights booked) and one to Chile, best scores and a daily streak. Being on another port, its data is kept apart from yours. The sample data is put in when nothing is saved there yet; add ?reset to the address to start over. It's only in this mode, not in the build.
| Command | What it does |
|---|---|
npm run dev | Start the dev server |
npm run dev:demo | Start the dev server with sample data, on port 5174 (see Getting started) |
npm run build | Type-check and build for production into dist/ |
npm run build:screensaver | Build the screensaver file and copy it to /Users/Shared/Meridian/ (see Screensaver) |
npm run preview | Serve the production build |
npm run lint | Lint with Oxlint |
npm test | Unit and component tests (Vitest) |
npm run test:watch | Same, re-running on changes |
npm run test:coverage | Unit and component tests with a coverage report |
npm run test:e2e | End-to-end tests against the real WebGL globe (Playwright) |
npm run data:map | Regenerate src/data/countries-50m.json (the country shapes, with lakes cut out) |
npm run data:extra | Regenerate src/data/extra-countries.json (places too small for the 1:50m map) |
npm run data:regions | Regenerate src/data/regions.json (states and provinces) |
npm run data:facts | Download capitals, population and area from the World Bank into src/data/country-facts.json |
npm run data:time-zones | Read each place's time zones from the system's zone.tab into src/data/time-zones.json |
npm run data:currencies | Download which currencies each place uses from the Unicode CLDR into src/data/currencies.json |
npm run data:rates | Download a recent day's exchange rates into src/data/rates.json, bundled for offline |
npm run data:languages | Download the languages spoken in each place from the Unicode CLDR into |
The first time you run the end-to-end tests, install the browser:
npx playwright install chromium
.github/workflows/tests.yml runs all of it on GitHub for every pull request and every push to main: lint, the unit tests, the build (which type-checks), and the end-to-end tests. .github/workflows/deploy.yml then puts each push to main online (see Online).
src/
App.tsx ties it together: globe, hover/selection, menu, games, camera flights
CountryPanel.tsx panel shown for the selected country
RegionPicker.tsx its states to tick off
CityPicker.tsx its visited cities, and a box to add more
FlagCorner.tsx hovered country's flag, bottom-right
Tooltip.tsx country name that follows the mouse
countries.ts every place: shape, names, codes, size, map color; lookup by point or name
flags.ts country → flag image URL
data/
names.ts display names, alternative spellings, countries vs territories
sovereigns.ts the country each territory belongs to (Greenland to Denmark)
continents.ts each place's continent
westernSahara.ts shows all of Western Sahara (see below)
countries-50m.json the country shapes, with lakes cut out
extra-countries.json Tuvalu and Gibraltar, from the 1:10m map
regions.ts states and provinces: names, lookup, loading (shapes in regions.json)
facts.ts capital, population and area (data in country-facts.json)
cities.ts big and well-known cities: loading, lookup (data in cities.json)
airports.ts airports with scheduled flights: loading, search (data in airports.json)
flights.ts flights: distances, figures, routes, moving old city flights to airports
trips.ts flights grouped into trips as worked out from where they go, and their stops
savedTrips.ts trips made by hand, the only record of visits: visits and flights in order, what's in none,
where something new goes, and what there was before (dates, plans) put into trips
replay.ts replaying your trips, oldest first, or one, as a timeline of flights flown and places reached,
on its own clock, to pause and go faster
localTime.ts each place's time zones and the time there (data in time-zones.json)
languages.ts the languages spoken in each place (data in languages.json)
money.ts each place's currency, what it's worth in yours, and a guess at yours
(data in currencies.json, rates bundled in rates.json)
storage.ts state saved in the browser
base64url.ts packing text into an address, for the screensaver and a friend's link
screensaver.ts the screensaver mode, and carrying your places in its address
demo.ts sample data for `npm run dev:demo`
ui/ the cards with "GAMES ··· 06" headers, the boxes of figures, the key in the globe's corner,
keeping password managers off the text boxes, and a clock that moves on each minute
nav/ the top bar, tabs, the column of cards on the right
explore/ the Explore tab's buttons: the atlas search, and the designs and layers, with their settings
visited/ visited countries, states and cities; the wishlist; a friend to compare with; flights, and the airport search;
achievements (achievements.ts), their view and the note when one is earned;
each year's review (yearInReview.ts) and its view; the cards to keep or share, a year wrapped
(wrapped.ts), a trip (tripCard.ts) or your atlas (atlasPoster.ts), drawn on a canvas
(cardDrawing.ts) and shown full screen
design/ design picker, how to set up the screensaver, and the way out of its preview
more/ the More tab's small cards, each opening to all of it
settings/ backups: making, checking and restoring them, and their card; installing the app;
your currency and the day's rates (useCurrency.ts), and their card
pwa/ the service worker (serviceWorker.ts, written into the build by vite.offline.ts),
starting it, and the browser's offer to install
games/ game rules (games.ts, letterGame.ts, higherLower.ts, daily.ts, cityGame.ts, cityCountry.ts, biggerCity.ts,
neighboursGame.ts), what the globe shows (globeView.ts),
state and best scores (useGame.ts), perfect runs and their times (records.ts),
the panel, answer box and outlines
globe/
sphereMesh.ts triangulating countries on the sphere
countryLayer.ts all countries merged into one mesh, plus borders and markers; the raised country
regionLayer.ts states and provinces drawn over their country
pinLayer.ts pins on visited cities, and finding the pin under the pointer
flightLayer.ts flight routes as arcs, with planes flying along them
colors.ts which color each country gets (game answers > hover > a friend's > visited > wishlist > land)
themes.ts the designs
hooks.ts the layers, pointer picking, depth precision, idle spin
interaction.ts click-vs-drag, flight duration/altitude, easing
picking.ts screen position → lat/lng on the globe
style.ts heights
e2e/ Playwright tests
scripts/ data extraction
.github/workflows/ the tests, run on GitHub, and putting the app online with GitHub Pages
A few choices keep the globe smooth:
The map has 243 places: the 197 countries (the 193 UN members, the observer states Vatican City and Palestine, and Kosovo and Taiwan) and 46 territories and other areas, such as Greenland, Puerto Rico, Hong Kong, Western Sahara and Antarctica.
Each place has:
name: the display name, e.g. "Eswatini". The map data (Natural Earth) abbreviates names ("Dem. Rep. Congo") and writes "eSwatini", the styling the kingdom itself uses; we show full current English short names.mapName: the name in the map data.aliases: every other name it goes by, from ISO and a curated list of former and common names ("Swaziland", "East Timor", "Ivory Coast").kind: "country" or "territory".continent: from flag-icons' country data, with the Caribbean islands it places in South America (Aruba, Curaçao, Bonaire, Trinidad and Tobago) counted as North America, as usual. Russia and Cyprus are in Europe; Türkiye, Georgia, Armenia and Azerbaijan in Asia.isoCode and isoAlpha2: ISO 3166-1 codes ("208", "DK"). A few disputed areas have none (null); Kosovo uses the widely adopted "XK".centroid, extent and areaKm2: center and size of the main landmass, and the area.tiny: under 2,500 km², so it gets a marker.island: shares no land border, so (under 30,000 km²) it gets a ring in the letter hunt.mapColor: 0–4, never shared with a neighbor.Some corrections to the map data:
scripts/extract-map.mjs cuts its 275 lakes at 1:50m out of the countries, and extract-regions.mjs out of the states, so they show as water and pointing at them finds no country. The cutting is done with Clipper on the map's own grid: every other point stays exactly where it was (so neighbors still share their borders), and points along borders that follow a parallel, like the 49th between the USA and Canada, are kept, as without them those borders would bulge into great circles on the globe. Borders that ran through lakes are now lake shores.scripts/un-borders.mjs follows the UN: Crimea is Ukraine's (General Assembly resolution 68/262), the Golan Heights are Syria's (Security Council resolution 497, cut along the 1967 line), the Chagos Archipelago is Mauritius's (resolution 73/295), and Somaliland and Northern Cyprus, which run themselves but are recognized by few countries, are part of Somalia and Cyprus. The countries involved say so in their panels. Disputes the UN takes no side in, like Kashmir, stay as drawn. Kosovo and Taiwan, which aren't UN members, are kept as countries, as in most lists of the world's 197.country-facts-extra.json.Local time and currency. Under a country's inhabitants and area, its panel shows the time at its capital ("06:42"), and how far ahead of yours ("7 h ahead of you"); a place with several times (the USA's 29 time zones are 6 times) says so, and that the one shown is the capital's. Beside it, what a round sum of its money is worth in yours: "100 JPY = 4.23 DKK", the smallest of 1, 10, 100… worth at least one of yours, with the currency's name, or "as yours" when it's the same. Across the bottom, the languages spoken there: the official ones first, then those official in a part of it (Catalan in Spain), each by how many speak it, or for a place with none official, the most spoken. The first four show, and "+17 more" shows the rest (India has 21). Your currency is a setting under More › Settings › Currency; until you pick one it's guessed from your device's time zone (set by where you are, so Copenhagen's is the Danish krone), else the region of your language, without asking for your location.
The time zones are the IANA time zone database's zone.tab (public domain), which lists every zone with its country and the place it's named for: scripts/extract-time-zones.mjs (npm run data:time-zones) reads the system's copy into src/data/time-zones.json, the zone nearest to each capital first; Kosovo, with none of its own, has Skopje's. Which currencies each place uses today come from the Unicode CLDR (Unicode License), the locale data browsers are built on (npm run data:currencies), and so do its languages, with how many speak each and whether they're official (npm run data:languages); their names are the browser's own. The rates, per euro, are the open currency-api (public domain): those of a recent day are bundled (npm run data:rates), so it works offline straight away, and the app fetches the day's from it at most once a day, only when you open a country's panel (or the Currency card) and are online. That's the one request the app makes to a third party; it carries nothing of yours. The panel says from which day the rates are.
Capitals, population (2024) and total area come from the World Bank's open data (CC BY 4.0). Places it doesn't cover (Taiwan, Vatican City, Western Sahara and several territories) use recent censuses and estimates from src/data/country-facts-extra.json, marked as estimates in the app. Somalia's and Cyprus's figures include Somaliland and Northern Cyprus.
Cities come from GeoNames (CC BY 4.0), via all-the-cities. For each place, scripts/extract-cities.mjs keeps the capital, every city of a million or more, the next biggest (more for more populous countries, from 50,000 people), and a hand-picked list of famous smaller ones (Venice, Key West, Chefchaouen…), leaving out suburbs within 25 km of a city already picked. GeoNames often uses local spellings, so the script has English names for well-known cities ("Cologne", not "Köln") and leaves out transliteration marks; it also has a short list of GeoNames entries that are districts, camps or campuses rather than cities. Overseas regions like Réunion are listed under the country the map draws them in. Cities in the USA, Canada, Australia and Brazil carry their state: GeoNames' state code for the city, matched to the map's state that most of that code's towns fall in (the city's own point won't do, as the map's simplified outlines leave cities on a coast or a border just outside). A few places the all-the-cities extract leaves out (Vilanculos) are added by hand, with their GeoNames ids.
How far each city reaches, for the city game, comes from Natural Earth's 1:10m urban areas (public domain). scripts/measure-cities.mjs lays a grid of square kilometers over the built-up land around each city, and counts the land reached from the city without a gap of more than 2 km, so a river or a strait doesn't cut a city in two (New York, Istanbul) but a town apart from it isn't counted. Where cities grow into each other, like Tokyo and Yokohama, each square goes to the city it's nearest to for its size, so the bigger one reaches farther. A city's reach is the radius of a circle as big as its land.
Airports come from OurAirports (public domain): scripts/fetch-airports.mjs keeps the large and medium airports with scheduled airline service and an IATA code, which covers every international airport.
Lakes come from Natural Earth's 1:50m lakes, also via sane-topojson.
The Realistic design's picture of the Earth is NASA's Blue Marble (public domain), 4096 × 2048, with a map of where the sea is to make it shine and one of how high the land is, for the relief. The lights at night, in every design, are kept from NASA's Earth at night: in that picture they're dots a pixel or two wide on moonlit land and sea, which would blur into the blue when shrunk to fit the globe, so scripts/make-night-lights.mjs (npm run data:night-lights) keeps only what's brighter than around it, on black, where it blurs into a glow instead. All of them come from three-globe's examples (src/assets/).
States and provinces come from Natural Earth's 1:50m states and provinces, which covers the USA (50 states and D.C.), Canada, Australia and Brazil. They're copied out of sane-topojson (MIT) and load in the background after the globe. More countries would need Natural Earth's much larger 1:10m dataset.
This repo uses Conventional Commits: feat:, fix:, refactor:, docs:, test:, chore:.
For the USA, Canada, Australia and Brazil you can also mark the states, provinces and territories you've visited: click "… states explored" in the country's panel and tick them, or click them on the globe. They're drawn over the country in a darker shade.
Countries you want to visit go on your wishlist: press "Add to wishlist" in a country's panel, or the ☆ next to a search result. They're colored on the globe in their own color (lilac in Midnight), and listed under Wishlist in the Visited tab with their continent. "Been there" moves one to your visited atlas, and visiting a place any other way takes it off the wishlist too.
Trips to come. A trip can be in the future: its places and flights in months still to come. Until then its places are tinted with the flight color (light blue in Midnight) and outlined in dashes on the globe, its flights drawn dashed with no plane yet, and it's counted down to in the Trips list ("next month", "in 3 months") and in each place's panel. Nothing in it counts in the figures, the years or the achievements until its month comes; then its places are in your visited atlas, as visits like any other. (Visits planned with "Plan a visit", before trips could be made, became trips to come, joining a trip whose flight lands there that month.)
Visits are made in trips, and only there: each place in a trip, with when, is a visit. A country's panel lists its visits, newest first (those to come first of all), each with the trip it was part of (its name, or its flags); click it to open that trip in the Trips list. The pencil beside a visit adds a note, like "honeymoon" or "rained all week" (up to 200 characters), shown under its date and next to the place in that year's review. The Visited list shows the latest ("3 visits, last May 2023"). A country a trip has been to stays in your visited atlas while the trip has it: its panel says so, and to remove it, take it out of the trip. A country marked visited by hand, with no trip, can be removed as before. (Visits dated before trips were made by hand went into trips once: with a trip whose flight landed there that month, or else a trip for each month.)
The heat map, a switch under "Visited countries" in the layers, shades your places by how many times you've been instead of one color. The shades go from near the land color for one visit to the full visited color for four or more, with a key at the bottom, beside "drag to spin" (or above it where there isn't room). A place marked visited without dates counts once. It gives way to a year shown in the Years view.
Every country's panel also has its visited cities, and a box to add more from its big and well-known cities (focus it to see the biggest). Each city you've visited gets a pin on the globe; point at a pin to see the city's name, or click it to open its country. Adding a city also marks its country, and its state, as visited. The state comes with the city (from GeoNames), so a city on a coast or a border gets its own even where the map's simplified outline leaves it just outside (New York City's point is in the harbour).
Under Trips, your visits and flights come together as you went. "+ New trip" makes one at once, opened at the top of the list, to add places and flights to straight away. While it has nothing in it, its name is a box where it shows ("Name it, if you like"), and later the pencil (below) names it. Left with nothing in it, and no name, for another trip, it's gone again, and "+ New trip" with one still empty opens that one. Each trip is a card: when, and small flags of the countries it went to, in order (for a trip of only flights, the countries they land in, home left out), after its name if you've given it one. Open one to see its places and flights in order, each with its date. Drag one by its grip (⋮⋮) to move it, with a mouse or a finger, or move it with the arrow keys on its grip. "+ Place" adds a place you went, and when (a month and year, or just the year; in the future for a trip to come); one not in your atlas yet goes in, once its month has come. "+ Flight" adds a flight, and when, from where the trip's last flight landed, and ⇅ swaps From and To for the flight back. Something new goes where it fits: a flight after the place it leaves from, or else before the one it lands in; a place after the flight that lands there, or else before the one that leaves from it. Drag it elsewhere if it's not right. Change a place's or a flight's date from its date, take a place out of a trip with × (it stays visited), or remove a flight with ×. The pencil names the trip ("Interrail 2019") and gives it a note, shown at the bottom of the globe when the trip, or one of its flights, is picked. "Show on globe" shows all of it, every flight highlighted, and ▶ replays it, as "Replay your trips" does all of them (below): its plane flies each flight, drawing it as it goes, and each place is colored as it's reached, the globe turning to each, with a bar at the bottom saying where it's got to ("Nairobi → Cape Town", "Zambia"). A trip without a date replays too. At the end the globe shows the whole trip, and on a phone the Trips list comes back, as it does with Stop. Escape, taking hold of the globe or going to another panel stops it too. "Share trip" makes it a card to keep or share, as a year wrapped is (under Years, below): its name, or where it went ("Japan & South Korea", "Japan, South Korea & 3 more"), on two lines if it's long, and when, counted down to for a trip to come; a small globe zoomed in on its places, with its flights drawn on it; how many countries, how many places were new on it (first visited then, or for a trip to come, not been to yet: in green on the globe, their flags ringed) and how many flights; the flags of its places in the order it went; and how far it flew, how many times around the Earth, and its longest flight. Save image downloads it, Share on a phone sends it to another app, and Escape or ✕ puts it away. "Remove trip" takes the trip apart; what was in it stays, in no trip.
Trips are newest first, by when they started, those with no date yet (one being made) at the top; those still to come are counted down to ("next month"). "▶ Replay your trips" plays every trip been on, from the first to the last: the globe turns to each flight as its plane flies it, drawing its line behind it as a trail, and to each place as it's reached, colored from then on, the places of the trip it's on in green; at the bottom, which trip of how many, and when, with Stop (Escape, or taking hold of the globe, stops it too), ⏸ to pause it (the planes stop where they are) and play on, and its speed, 1×, 2× or 4×, faster each time it's pressed, kept for the next replay. The replay runs on a clock of its own, which the steps, the planes and their trails all follow, so it pauses and speeds up as one. On a phone the list makes way, and comes back at the end. Under the trips, "Not in a trip yet" lists the countries marked visited that no trip has been to, each once, by name, and any flights in no trip: put them in a trip with "+ Trip", or a new one, and give them a date there. Each visit and flight is in one trip at most. A visit is a place and a month or year, so a country you've been to three times is three visits, which can be in three trips.
Flights are between airports: every international airport, and the regional ones with airline service (3,244 in all). Search by city, airport name or code ("Copenhagen", "Heathrow", "CPH"); each result shows the airport's name and country. A flight can have a month and year ("When"), kept for the next one. Each route is drawn on the globe as a thin arc, rising with the distance; a route flown both ways or more than once is drawn once. A little plane flies each trip, slowly (Copenhagen to Bangkok takes about half a minute) and turned the way it's going: its flights in the trip's order, then over again. It's there only while flying: it goes once it lands, and appears again as it sets off on the next flight. One plane a trip keeps the globe calm however many flights there are. The figures are how many trips, how many flights and how far as the plane flies (pointing at it says how many times around the Earth). Click a flight to see its route from above, highlighted, until you press Escape or click the globe. Adding a flight doesn't mark where it lands as visited: changing planes isn't visiting. (The first flights were saved between cities; they move to the city's main airport by themselves.)
Trips used to be worked out from the flights, not made by hand. The first time the app opens with trips, it makes them from the flights there are, as they were worked out (legs that each leave from where the last one landed, within 100 km, and within a month of each other, or overland up to 1,000 km, ending back where they started), with the names they had, and as their places the countries landed in where you've a visit then (or one without a date). Flights in no trip are flown by planes grouped that same way.
Under Years, a review of each year you've dated a visit or a flight, from the newest, with ‹ › for the others. It has:
The places follow month by month, and those dated only by the year come last; click one to open it, or the longest flight to see its route. While Years is open, the globe shows just that year: its places, those first visited that year in green and those you went back to in a darker green (with a key at the bottom), and its flights, even with those layers off. Picking a year turns the globe to face them, zoomed out enough to see them all (or as far as it goes, for a year all around the world), and the globe stops spinning on its own until you leave. States, city pins and the wishlist are hidden, as they have no dates. Places and flights without a date aren't in any year.
"✨ Your 2025, wrapped" makes the year a card to keep or share, in the style of a phone's story (1080 × 1920): the year in big type, a small globe turned to its places (those new that year green, as many as the "new" figure, and those visited before in the visited color), how many countries, how many new and on how many continents, the flags of its places (the new ones ringed, up to 18), and its flights (how many, how far, how many times around the Earth, the longest) or else its busiest month. It's drawn on a canvas in the app's own colors. Save image downloads it as a PNG, and on a phone Share sends it straight to another app; Escape or ✕ puts it away.
"▶ Replay 2024" replays the year's trips on the globe, as "Replay your trips" in Trips does all of them: each flight flown by its plane, drawing it as it goes, and each place colored as it's reached, the globe turning to each, with the bar at the bottom ("Your 2024, from the first trip", then each trip, "1 of 3"), to pause, play faster or stop. A trip that goes over the new year is in both. At the end the globe shows the year again; leaving the Years view stops it.
Games: the daily challenge first, then the others by what they ask about: countries, flags, capitals, cities and facts. Each kind is a card saying how many games it has, opening a list of them (or the game itself, for a kind with one), so the start fits without scrolling. A game's setup goes back to its kind's list, and that back to all games.
Games played in rounds (all but the letter hunt, "name them all", higher or lower and bigger city) are Easy (big countries, four answers to pick from), Medium (all but the smallest), Hard (all 197) or All countries (every one of the 197, one after another; stop whenever you like). Beyond Easy you type answers with no suggestions; any known spelling counts ("East Timor", "Burma", "Ceylon"), punctuation and spacing don't matter, and the answer shows the name used today. Tiny countries that are answers get a dot so you can see them. Rounds have an "I don't know" button that shows the answer; the round counts as wrong. Every game is about the 197 countries only: clicking or typing a territory (Greenland, Puerto Rico…) counts neither way, and territories don't light up under the pointer or get a ring.
A clock runs while you play. Perfect runs set a time record to beat, next to the best score: every point (in Find the country, every country on the first try; in Find the city, every city spot on; in Neighbours, every neighbour without a mistake), no wrong letters in the letter hunt, and played to the end. Being fast with a mistake doesn't count. The clock stops at the last answer, not when you look at the results. Higher or lower keeps its longest streak instead.
More: a small card for each thing, opening to all of it, with "← Back" to go back: your achievements and comparing with a friend, then, under Settings, your currency (below), backing up everything to a file or restoring a backup (below), installing Meridian as an app that works offline (below), and how to make the globe your Mac's screensaver.
Achievements: 62 to earn from where you've been (a territory counting for its country, so the Faroe Islands are Denmark for Scandinavia), in nine groups: milestones (your first country, then 10 up to all 197), continents (every continent, all of one, Antarctica, all four hemispheres), regions (Scandinavia, the Nordics, the Baltics, Benelux, the Caribbean, the Gulf, the Stans, the G7 and more), islands, states (every US state and D.C., all of Canada, Australia or Brazil), cities and capitals, flights and distance flown (around the world, to the Moon, long haul), trips (10 trips, 5 countries in one trip, a trip to every continent, and one trip all the way round the Earth, its flights going east or west 360°), and return trips (one country 3 or 5 times). Each shows how far along you are ("3 of 5"), and a note pops up at the bottom when something you add earns one; click it to see them all. They're worked out from what you've saved, so a backup brings them back too.
Compare with a friend puts your places and a friend's on one globe. Each of you copies your link ("Copy my link", with your name if you like) and sends it in any chat. The other pastes it in there, or just opens it on the online version, where you're both at the same address. Only your places go in the link (not your cities, flights or dates), packed into its #compare=… part, so nothing goes through a server. The globe then colors where you've both been green, where only you have been in your color, and where only your friend has been in theirs (the wishlist's color, as the wishlist steps aside meanwhile), with a key at the bottom. It shows "You 42 · Both 18 · Anna 24" and the places in each group, and a star puts a place only your friend has been on your wishlist, or takes it off again. Only countries are compared, so while comparing is open the globe shows just countries: your states, city pins and flights step aside, whether your friend is shown or not. Your friend is on the globe only while comparing is open; going back, or another tab or panel, puts them away, and opening it again shows them again. They stay until you remove them, off the globe until switched on.
src/data/languages.jsonnpm run data:cities | Regenerate src/data/cities.json (each place's big and well-known cities, with their states where the map has them) |
npm run data:city-sizes | Measure how far each city in src/data/cities.json reaches (run it again after data:cities; downloads Natural Earth's urban areas, 28 MB, or give their path) |
npm run data:airports | Download the airports with scheduled flights from OurAirports into src/data/airports.json |
npm run data:icons | Draw the app icons in public/icons/ (192, 512, and 180 for iPhone) from public/icon.svg |
npm run data:night-lights | Keep the lights of NASA's Earth at night, on black, in src/assets/earth-lights.jpg, for the night in every design |