New: Photo Explorer · Find any mapped travel photo

Engineering update

Photo Explorer, Constellations, and a Faster Mapsake

Two ambitious photo features exposed the same question: could Mapsake do substantially more work without becoming slower? A new simulator-and-device benchmark suite turned the answer into measured engineering work.

By 13 minute read
Mapsake Photo Explorer with filters, collections, map and timeline controls, and travel photo results

A feature release and an engineering release

This update introduces two of Mapsake's most ambitious photo experiences: Photo Explorer, which makes a large mapped library searchable, and Constellations, which discovers visual threads between distant places.

It also includes the least visible feature in the release: a permanent performance benchmark suite. That suite immediately found the slowest shared paths, made the fixes measurable, and gave the app a way to detect when future work makes them slower again.

The result is not just more capability. Mapsake is substantially faster at turning large photo libraries into places, finding nearby cities, and planning a constellation sky.

Photo Explorer searches the travel record, not the camera roll

Photo Explorer begins with the compact metadata Mapsake already maintains for mapped photos. That includes location, date, source, camera, notes, elevation, speed, favorites, screenshots, edits, and the organization added inside the app.

The index stays on the device. A search can be direct—Japan, 2024, iPhone, favorites—or phrased more naturally, such as “favorite photos from Italy last year.” The query layer turns supported language into a structured set of filters, while deterministic vocabulary and typo correction provide a reliable fallback.

Search is only one route into the library. Collections expose useful groups without typing; Map Area limits results geographically; Timeline groups them by date; saved and recent searches make repeated questions cheap. Favorites, ratings, tags, labels, and notes sit in a local sidecar so organizing a photo does not alter or upload the original.

The important architectural choice is that each tool shares the same indexed snapshot. Collections, timeline, map, and text search do not each rebuild an 82,000-photo world from scratch.

The index is derived, while personal organization is durable

Photo Explorer needed two kinds of storage with very different lifetimes. Search fields such as place names, capture year, camera, elevation, and source can be rebuilt from Mapsake's existing metadata. A favorite, private note, rating, tag, or color label was written by a person and cannot be treated as disposable cache data.

Mapsake therefore keeps user-authored organization in a small backup-eligible sidecar. The larger search index lives in Caches, contains no image bytes, is excluded from backup, and can be regenerated whenever its schema changes. Re-scanning a library or clearing derived data does not erase the work someone put into organizing it.

That division also made backups more honest. Lossless JSON and HTML backups can carry notes and organization, but they do not quietly balloon into photo archives. Apple Photos annotations retain a best-effort stable iCloud identifier so a restored backup can reconnect to the local copy on another Apple device; Immich asset identifiers are already stable at their source.

The first full stress fixture produced a compressed index of about 12.4 MB for 82,000 photos. A cold geographic build took 2.61 seconds on the development simulator, a later restore from disk took 1.20 seconds, generated collections took 126 milliseconds, and the daily timeline model took 211 milliseconds. Those numbers gave each part of the feature its own budget instead of hiding everything behind one generic “search” measurement.

Photo Explorer also explains why an item matched. A result can say that it matched Kyoto, 2024, an iPhone camera, a private note, or a selected filter. That little line is important when a query combines natural language with several exact controls: the user should never have to guess what the search engine thought they meant.

Search language is an interface, not a license to improvise

Every supported query ultimately becomes a validated filter structure. The direct path recognizes places, dates, sources, camera models, notes, favorites, ratings, tags, elevation, speed, screenshots, and edited photos. A bounded spelling corrector can repair intent words and known indexed vocabulary, but it does not rewrite arbitrary private notes.

On devices that support Apple's on-device Foundation Models, the submitted text can also be interpreted into the same constrained structure. Only the query and current year are provided to that system model—not the photo pixels, metadata index, location notes, or personal organization. Invalid ranges and unknown values are rejected, and the deterministic parser remains the fallback.

The interface shows the interpretation and offers a route back to the original words. This is a place where cleverness is useful only when it stays inspectable. “Photos above 3,000 meters from last year” should feel conversational, but it should still behave like a precise set of filters.

Constellations looks for repetition across distance

Then & Now asks whether someone returned to the same location. Constellations asks almost the opposite question: what visual ideas did they repeat in places far apart?

The on-device index extracts a compact set of visual signals and motifs from eligible travel photos. The planner looks for features that are distinctive for one image rather than merely common everywhere, then connects candidates across different destinations. Doorways, coastlines, skylines, mountain forms, colors, seasons, and composition can become the vocabulary of a thread.

Those threads are arranged into a three-dimensional sky. The user can orbit through it, open a constellation, keep or dismiss a connection, and play a match-cut film in which related photos dissolve from one place into the next. Share cards and a reel use the same saved thread data.

The extraction and planning work happens on the device. Mapsake does not send a travel library to an image-analysis service. Background batches can deepen the index over time without making first launch wait for the entire library.

The visual index was designed to be retuned cheaply

For each eligible representative, Mapsake performs one small image decode and derives several signals: a Vision feature print, raw classifier labels, a compact color palette, and a light class estimated from location, time, and solar elevation. The feature print is a small numeric description used for similarity; it is not a copy of the picture and cannot be displayed as one.

The index stores raw classifier identifiers rather than immediately replacing them with product-facing motifs. That choice paid off during real-library testing. The first motif list included labels that sounded reasonable but were not actually present in Vision's supported taxonomy. Because the raw labels were still available, rebuilding “Towers and bridges,” “Boats and harbors,” and later motif groups was a fast scoring pass rather than another scan of thousands of originals.

Indexing begins with representative photos rather than reading the library strictly from newest to oldest. Photos are grouped into approximate place cells and place-days, with preference for useful still images and spacing between bursts. The planner then moves across places in rounds. That gives a young index geographic breadth while background batches gradually add depth.

The first version chose one photo per place in each round. Real device data revealed the flaw: most places remained below the three-photo minimum used by the thread engine, so thousands of indexed assets could still produce no candidates. Chunking each round to three representatives made the index useful sooner without increasing its total budget. This is precisely why synthetic fixtures and a real, messy library both matter.

The extractor works in serial batches, commits progress atomically, pauses for thermal pressure, and skips background extraction in Low Power Mode. Apple Photos thumbnails already on the device are tried first; iCloud-only images can wait for a later network-permitted pass. Immich uses the same visual pipeline through its existing thumbnail client.

Similarity alone does not make a story

A feature-print distance can find two visually similar photographs, but Constellations is meant to find a connection between places rather than a duplicate detector. Candidate photos must belong to destinations at least 150 kilometers apart. The engine also considers distinctive motifs, unusual light, season, and recurring calendar rituals before it admits a thread.

“Distinctive” became the hard part. An early scoring model rewarded signals that existed in both places. On the development library, that produced hundreds of connections dominated by night and common seasons—technically shared, but not remotely surprising. The engine now evaluates lift: a signal matters when it is unusually strong in both places compared with the person's library as a whole.

That changed the question from “Do both places contain night photos?” to “Are both places unusually night-heavy for this library?” Common signals fade into the background, while repeated blue hour, doorway forms, harbors, winter light, or a recurring holiday week can become meaningful.

Visual candidates use several representatives per place rather than a single lucky pair. Thread identifiers are derived from their places and family, so the same connection retains its identity after a re-score. Kept, dismissed, and seen state therefore survives tuning, and a dismissed thread does not reappear merely because the engine ran again.

The sky layout is also precomputed and deterministic. Geographic positions seed the nodes, thread strength pulls connected places toward one another, and a small seeded jitter prevents exact overlaps. The live interface can animate between globe-like geography and the finished constellation with one transition value; it does not run a battery-hungry physics simulation every frame.

The match-cut cinema uses Vision attention saliency to place the gentle camera movement around the important part of each exemplar. A doorway dissolves toward another doorway instead of blindly aligning two image centers. Reduce Motion removes the drifting move and shortens the transition, while a center crop remains the graceful fallback whenever saliency is unavailable.

Why build a benchmark suite now?

Large-library performance had reached the point where intuition was no longer enough. A change could make one screen feel faster while quietly making imports, memories, or Constellations slower because they all depend on the same geographic and photo pipelines.

The new suite has two lanes:

  • A logic lane runs deterministic feature engines against small, medium, and stress fixtures, including an 82,000-photo tier based on the largest real-world report available during development.
  • A UI lane drives representative screens while Mapsake's own performance instrumentation records operation timing, hitches, and memory.

Benchmarks use release optimization with testability enabled. Debug builds are deliberately excluded because unoptimized Swift produces numbers that are not relevent to the shipped app. Simulator and physical-device results also keep separate baselines so unlike hardware is never compared as if it were the same environment.

The suite covers the gazetteer, map geometry, imports, photo metadata, achievements, Passport, Friends, backups, stamps, Constellations, memories, and Photo Explorer. Live networks, camera quality, system photo enumeration, and CloudKit remain integration tests because pretending those inputs are deterministic would make the results less honest.

A benchmark can be confidently wrong

Building the suite took more than wrapping a stopwatch around app code. The first derivation fixture used ordinary hashing to choose coordinates; Swift deliberately randomizes that hash between processes, changing geometry work by roughly 30 percent from one run to another. The fixtures now use a fixed generator, and the suite validates the generated shape before accepting a result.

Another early scenario named import.merge measured fresh inserts. The production path it was meant to represent was an idempotent merge into an existing atlas. The benchmark was fast and repeatable, but it was testing the wrong thing. Correcting the fixture changed the baseline before any optimization was attempted.

UI measurements had a similar trap. Performance counters accumulated from launch, so a tab gesture could inherit hitches from the launch animation and appear slower than it was. Each interaction now flushes its pre-gesture snapshot, then folds only the work inside that scenario's own context.

Runs record environment, operating system, fixture tier, build, and thermal state. A simulator result is never promoted over a device baseline. Non-nominal thermal runs remain useful diagnostics, but they are annotated instead of failing a regression gate. A regression must exceed both a relative threshold and a small absolute floor so sub-millisecond noise does not become a false emergency.

These details are not bureaucracy around the benchmark. They are what make the number worth acting on.

Correctness guards found a real map bug

The most valuable test in the first performance pass did not measure speed. It compared the optimized photo-to-place derivation with a deliberately simple frozen reference over border-heavy coordinates.

That guard failed before the optimization had even changed the engine. Photos inside overlapping administrative polygons—Berlin within Brandenburg, Seoul within Gyeonggi, and Kyiv City within its surrounding oblast—could be assigned according to the random iteration order of a dictionary. The same coordinate could resolve differently after a launch.

Both the reference and production engine now order overlapping candidates by polygon area, allowing the most specific administrative feature to win deterministically. Only after 82,000 fixture photos produced zero differences, with warm and cold paths agreeing, did the spatial shortcuts become acceptable.

This is the quiet advantage of performance engineering with equivalence checks: it can uncover a correctness problem that normal timing work would never see.

What the first measurements changed

The initial stress run exposed several shared bottlenecks. The first optimization pass added indexed geometry hit testing, a persistent geographic-cell cache, memoized place resolution, shared photo snapshots, and incremental Atlas annotation updates.

On the simulator, cold derivation of an 82,000-photo map fell from 34.5 seconds to 8.0 seconds. A warm repeat—the common path after a filter change or Places open—fell to 2.8 seconds. Constellation planning at the 10,000-photo tier fell from 3.3 seconds to 0.9 seconds. Tab-cycle hitch time dropped 31 percent, and Passport pager hitches dropped 47 percent.

The next pass replaced repeated SQLite latitude-band scans with a lazy, latitude-sorted nearest-city index and a bounded record cache. In the simulator benchmark, a medium nearest-city batch fell from 2.11 seconds to 5.2 milliseconds. On a physical iPhone, the same shared engine fell from 3.06 seconds to 6.35 milliseconds, while stress-tier constellation planning fell from 5.60 seconds to 65.9 milliseconds.

That second pass also pushed the 82,000-photo cold derivation from 7.96 seconds to about 1.00 second on the simulator, with a warm run at 984 milliseconds. The gains cascaded because nearest-city lookup sits underneath imports, Then & Now, map derivation, and Constellations rather than belonging to one screen.

These are controlled benchmark workloads, not a promise that every device or library will produce the same number. Their value is comparability: fixed fixtures, recorded thermal state, committed results, and gates that can flag a meaningful regression.

Faster because the work became more reusable

The largest wins did not come from removing features or adding a loading spinner. They came from stopping repeated work:

  • Region geometry now narrows candidate polygons before expensive point tests.
  • A build-keyed geographic cell cache remembers resolved areas across runs.
  • Nearest-city lookup uses a numeric spatial index instead of rebuilding a database sort for every coordinate.
  • Photo-driven surfaces share one immutable filtered snapshot.
  • Atlas pins and flags update by identifier rather than removing and recreating everything.

Photo Explorer and Constellations both benefit because they sit on top of these same foundations. So do imports, Then & Now, data maps, and place stories.

The benchmark code is excluded from App Store archives, but the habit remains in the repository: run the suite around changes to a hot path, compare with the correct baseline, and keep the result. Performance is now something the project can test, not just something it can hope to notice.

The numbers also preserve the unfinished work. A later physical-device UI pass reached serious thermal state and still recorded a large tab-cycle memory peak and nearly a second of hitch time opening Constellations against the real library. Those readings are diagnostic rather than a clean regression comparison, but keeping them visible is more useful than declaring the app “done.”

What these three projects have in common

Photo Explorer, Constellations, and the benchmark suite all began with the same constraint: a large travel library should become more useful without leaving the device or making the rest of the app feel heavier.

Photo Explorer turns known metadata into questions and collections. Constellations turns representative image signals into a visual story. The benchmark suite makes both accountable to the shared engines beneath them.

The feature work is visible in a search result, a starry sky, and a match cut. The engineering work is visible mostly in what does not happen: a launch does not wait for all 82,000 photos, a filter does not rebuild five copies of the same array, and a fast spatial index does not silently change which city a photo belongs to.

That is the direction I want performance work to take in Mapsake. Speed is not a cleanup phase after ambitious ideas. It is one of the tools that makes those ideas safe to keep.

Mapsake app icon

Independent developer of Mapsake, writing from the product, mapping, privacy, and Apple-platform work behind the app.

Build your own atlas

Start with the travel history you already have.

Mapsake is free, requires no Mapsake account for core use, and keeps supported photo matching on your device.

Get Mapsake free