# TESSERA now supports the Zarr geo-embeddings convention proposal

*2026-03-27 — note*


The ink's barely dry on my last [RFC](https://en.wikipedia.org/wiki/Request_for_Comments) post about [streaming millions of TESSERA tiles over HTTP](https://anil.recoil.org/notes/tessera-zarr-v3-layout) using Zarr v3 sharding, and it's out of date! The store layout I'd carefully craftd lasted about 48 hours before [a discussion](https://github.com/ucam-eo/zarr-convention-tessera/issues/1) from [Jeff Albrecht](https://github.com/geospatial-jeff) and [Len Strnad](https://github.com/ljstrnadiii) convinced me to rethink my approach.

This led me to build a "MegaZarr" unified store, and retire our TESSERA-specific convention to instead integrate with a shared [geo-embeddings Zarr convention](https://github.com/geo-embeddings/embeddings-zarr-convention) that's appeared in the last week.  Things are moving *really* quickly in the world of geospatial embeddings; I'll explain some of the gory details about the store here, and here's a video to show it in action or **[try it for yourself](https://tze.geotessera.org)**:

<div class="video-center"><iframe title="" width="100%" height="315px" src="https://crank.recoil.org/videos/embed/e5b98a04-9f08-446b-9cfd-b15d8d441d5e" frameborder="0" allowfullscreen sandbox="allow-same-origin allow-scripts allow-popups allow-forms"></iframe></div>

## Arise, the MegaZarrrrr

The [original layout](https://anil.recoil.org/notes/tessera-zarr-v3-layout) had a separate Zarr store per year, an HWB array layout, and 256-pixel shards with very fine tile granularity. After the discussions, instead of a Zarr store per year, we now have a **single store with time as a first-class dimension**, which I dubbed [the MegaZarr](https://www.linkedin.com/feed/update/urn:li:activity:7442572588876439552?commentUrn=urn%3Ali%3Acomment%3A%28activity%3A7442572588876439552%2C7442624868975804416%29&dashCommentUrn=urn%3Ali%3Afsd_comment%3A%287442624868975804416%2Curn%3Ali%3Aactivity%3A7442572588876439552%29). The unified array shape is now `(T, B, H, W)` or [NCHW ordering](https://stackoverflow.com/questions/67087131/what-is-nchw-format) which is easier to use in ML frameworks like PyTorch. Each year's data lives in its own temporal slice, and the `time` coordinate array stores the years explicitly so it can just be indexed by the integer year directly.

You can see this in the video above in the new 'explorer' mode, whereby you can just go to <https://tze.geotessera.org> and click around individual tiles and 10m² pixels and see the embeddings all the way from 2017 to 2025 where available.  We can't easily prepend years to an existing store without rewriting the whole thing, so we zero-fill the entire year range upfront and selectively replace years as they're generated. We use dual sentinels in the scales array to do this via `+inf` for "land but no data yet" and `NaN` for water areas that don't have valid embeddings.
This lets a single `isfinite()` call identify valid terrestrial pixels in a tile.

Switching from HWB to NCHW format also aligns with how ML frameworks expect data since all 128 bands for a pixel are contiguous in a single inner chunk, which is what downstream tasks usually need. The common operation of grabbing a single year is fine since the time dimension coming first means temporal slicing is cheap.

I've also tweaked the shard/chunk balancing sizes to a larger 4096×4096 shard size vs the original 256×256, and a slightly larger inner chunk of (1,128,32,32). There are still only two HTTP range requests for a single pixel, but now each shard covers a larger (around 40km²) area with far fewer files on the server. The [TZE explorer](https://tze.geotessera.org) confirms this still works reasonably well on mobile.

<figure class="image-center"><img src="/images/ssds-by-the-dozen.webp" alt="Mark Elvers is installing SSDs today by the bushel so we can keep up with all the storage requirements here. We do need cloud hosting for all this Zarr as we're over a petabyte raw now in the University and it's nervous work..." title="Mark Elvers is installing SSDs today by the bushel so we can keep up with all the storage requirements here. We do need cloud hosting for all this Zarr as we're over a petabyte raw now in the University and it's nervous work..." loading="lazy" srcset="/images/ssds-by-the-dozen.768.webp 768w, /images/ssds-by-the-dozen.640.webp 640w, /images/ssds-by-the-dozen.480.webp 480w, /images/ssds-by-the-dozen.320.webp 320w, /images/ssds-by-the-dozen.2560.webp 2560w, /images/ssds-by-the-dozen.1920.webp 1920w, /images/ssds-by-the-dozen.1600.webp 1600w, /images/ssds-by-the-dozen.1440.webp 1440w, /images/ssds-by-the-dozen.1280.webp 1280w, /images/ssds-by-the-dozen.1024.webp 1024w"><figcaption>Mark Elvers is installing SSDs today by the bushel so we can keep up with all the storage requirements here. We do need cloud hosting for all this Zarr as we're over a petabyte raw now in the University and it's nervous work...</figcaption></figure>

## New geotessera tooling around Zarr

All this also means that all my hard work on [geotessera](https://anil.recoil.org/notes/geotessera-python) will also soon be obsolete! A quickstart to grab embeddings using _just_ xarray/Zarr from Python looks like

```
# params, year (int), x, y
ds = xr.open_zarr(args.store, group=f"utm{zone:02d}",
       zarr_format=3, consolidated=True,
       chunks={"time": 1, "band": 128, "y": 4096, "x": 4096})
pixel = ds.sel(time=year, x, y, method="nearest")
emb_int8 = pixel["embeddings"].values
scale = float(pixel["scales"].values)
embedding = emb_int8.astype(np.float32) * scale
for i in range(min(16, len(embedding))):
  print(f"{i:4d}  {int(emb_int8[i]):5d}  {embedding[i]:10.6f}")
```

A few things of note here are that the use of coordinate arrays means that you can index into the Zarr array using the natural year integer and the UTM x/y coordinates.  After that you dequantise using the scales array, and the dimensions are in an array for you.

Now, there is still some useful housekeeping whereby Geotessera will continue to be useful for Zarr. Firstly, it can provide coordinate lookups and also a name resolver to find a copy of the embeddings (we are getting offers to mirror and version from several other people, which is great). Secondly, the biggest downside to Zarr is that it doesn't have a quick index of sparsity, which means that coverage tests can be expensive in terms of HTTP HEAD requests. So I suspect I'll keep the existing GeoParquet tile registry around to provide a quick lookup for clients that want to test large-scale tile existence. However, downloading this index will be optional so clients can get started immediately without an expensive database check.

Using the new wrapper (which I'm tidying up for a release) means you just need to:

```python
from geotessera.store import GeoTesseraZarr

gt = GeoTesseraZarr()  # defaults to public store
emb = gt.sample_at(-2.97, 53.44, year=2025)          # (128,) float32
mosaic, transform = gt.read_region(bbox, year=2025)  # (H, W, 128)
```

## From a TESSERA convention to a community one

While I was iterating on the store layout, a [Clark University hackathon](https://anil.recoil.org/notes/2026w12) on geo-embeddings created an [embeddings-stac-specification](https://github.com/geo-embeddings/embeddings-stac-specification) repository to connect STAC with vector embeddings. We had a [productive discussion](https://github.com/geo-embeddings/embeddings-stac-specification/issues/9) about how TESSERA's QAT-compressed embeddings differ from other models.

There's also another [geo-embeddings Zarr convention](https://github.com/geo-embeddings/embeddings-zarr-convention), which is a model-agnostic standard for describing geospatial embeddings in Zarr stores. It currently has examples for [Clay](https://clay.earth) (768-dim chip embeddings on a regular grid) and [AEF](https://developers.google.com/earth-engine) (64-dim patch embeddings with global quantisation).

To make TESSERA fit into this, I [proposed several additions](https://github.com/geo-embeddings/embeddings-zarr-convention/compare/add-tessera) to the convention:

1. Per-pixel scale quantization (`"method": "per_pixel_scale"`). Unlike the other models, TESSERA uses [quantization-aware training](https://anil.recoil.org/papers/2025-tessera) with a per-pixel scale array, and not a single global scale. The convention therefore now supports a `scale_array` field pointing to the array name and a `nodata` field for sentinel values.
   
2. UTM zone spatial layout (`"spatial_layout": "utm_zones"`). In order to minimise distortion due to varying pixel sizes at different points of the earth, TESSERA stores each UTM zone as a separate group rather than reprojecting to a global grid. This is a bit less seamless than the simpler `global` layout, but prioritises accuracy until we can measure the effect of the distortion. We use [Zarr groups](https://zarr.readthedocs.io/en/latest/user-guide/groups/) for this, so the [root store](https://dl2.geotessera.org/zarr/v2/store.zarr/) has subgroups with utmXY that the client opens as needed.
   
3. Multiple source datasets. TESSERA fuses Sentinel-1 (radar) and Sentinel-2 (optical), so `geoemb:source_data` now accepts an array of URLs rather than just one.
   
4. Build version tracking. We're going to be releasing multiple variations and models of our embeddings as the technology advances, so `geoemb:build_version` also records which version of the pipeline produced the store. I'm hoping to use this to record [end-to-end provenance](https://anil.recoil.org/notes/principles-for-collective-knowledge) for every single embedding.

See the [TESSERA example](https://github.com/avsm/embeddings-zarr-convention/blob/beb2da66d68eebf42a2bbef928bb7cde181570ba/examples/tessera_example.json) JSON for all this in one place.
With the `geoemb:` convention covering everything we need, the [zarr-convention-tessera](https://github.com/ucam-eo/zarr-convention-tessera) repo can now be retired after a glorious two weeks of life. The [Zarr web inspector](https://inspect.geozarr.org/?url=https%3A%2F%2Fdl2.geotessera.org%2Fzarr%2Fv2%2Fstore.zarr%2F) confirms our live store is compliant, except for those in-flight proposals that still need to be ratified.

## What's next

Thanks to everyone in the Zarr community for their really useful input. I wouldn't be surprised if we need to tweak my (newbie) layout a bit more yet, but comments are welcome [on my PR](https://github.com/geo-embeddings/embeddings-zarr-convention/pull/2). Once things settle, I'm back to working on the [OxCaml Zarr library](https://anil.recoil.org/notes/2026w9) to add the sharding support I need.

Also to throw a bit of a clanker into my delicate Zarr work, we're working on a revision of the TESSERA model that supports [Matryoshka representations](https://arxiv.org/abs/2205.13147), which means I need to figure out how to support slicing by dimension as well. After all this careful work in v1.0 to make the embeddings contiguous in memory, we will soon need to be able to get just the first few dimensions out of 128 to do quick sample analyses. Code doesn't survive very long any more with all the progress in training\!

The live Zarr v3 TESSERA store is browsable now at the [TZE explorer](https://tze.geotessera.org), and the embeddings convention is open for feedback at [geo-embeddings/embeddings-zarr-convention](https://github.com/geo-embeddings/embeddings-zarr-convention).
Synopsis: Community feedback reshaped our Zarr store layout — years became a dimension, shards got bigger, and we retired the TESSERA-specific convention in favour of a shared geo-embeddings standard that also covers other models.
Words: 1314
DOI: 10.59350/c3hrq-zsx02

## Related

- [Celebrating a year of Tessera embeddings and releasing GeoTessera 0.10](https://anil.recoil.org/notes/geotessera-a-year-on) (note, 2026-08-27)
- [.plan-26-32: Finally a use for serverless and found the Forester for the trees](https://anil.recoil.org/notes/2026w32) (note, 2026-08-09)
- [.plan-26-28: What fun papers piled up while I was out at sea](https://anil.recoil.org/notes/2026w28) (note, 2026-07-12)
- [A scorching CNG London during Climate Action Week](https://anil.recoil.org/notes/cng-london-2026) (note, 2026-06-24)
- [Tessera at CVPR 2026, and the front page of the European Space Agency!](https://anil.recoil.org/notes/cvpr-tessera-esa) (note, 2026-06-06)
- [TESSERA: Temporal Embeddings of Surface Spectra for Earth Representation and Analysis](https://anil.recoil.org/papers/2025-tessera) (paper, 2026-06-01)
- [.plan-26-17: Unwedging kernels, dogfood deployments, and managing beef leakage](https://anil.recoil.org/notes/2026w17) (note, 2026-04-26)
- [.plan-26-16: Chennai, Cambridge, Belfast: a week on the wing](https://anil.recoil.org/notes/2026w16) (note, 2026-04-19)
- [.plan-26-15: Banyan trees, (anti)botnets and Bose-Einstein bases](https://anil.recoil.org/notes/2026w15) (note, 2026-04-12)
- [.plan-26-14: Tracking AI screen time and escaping to pen and paper](https://anil.recoil.org/notes/2026w14) (note, 2026-04-05)
- [.plan-26-13: Oxidised, standardised, and syndicated](https://anil.recoil.org/notes/2026w13) (note, 2026-03-29)
- [TESSERA Zarr v3 streaming (take 2)](https://anil.recoil.org/videos/e5b98a04-9f08-446b-9cfd-b15d8d441d5e) (video, 2026-03-27)
- [.plan-26-12: Zarr across space and TESSERA time](https://anil.recoil.org/notes/2026w12) (note, 2026-03-22)
- [Streaming millions of TESSERA tiles over HTTP with Zarr v3](https://anil.recoil.org/notes/tessera-zarr-v3-layout) (note, 2026-03-14)
- [.plan-26-09: Browser TESSERA, package management and Docker in the CACM](https://anil.recoil.org/notes/2026w9) (note, 2026-03-01)
- [Four Ps for Building Massive Collective Knowledge Systems](https://anil.recoil.org/notes/principles-for-collective-knowledge) (note, 2025-11-23)
- [GeoTessera Python library released for geospatial embeddings](https://anil.recoil.org/notes/geotessera-python) (note, 2025-08-31)
- [OxCaml Labs](https://anil.recoil.org/projects/oxcaml) (project, 2025-01-01)
- [TESSERA, a pixelwise geospatial foundation model](https://anil.recoil.org/projects/tessera) (project, 2025-01-01)
- [Remote Sensing of Nature](https://anil.recoil.org/projects/rsn) (project, 2023-01-01)

---
Canonical: https://anil.recoil.org/notes/tessera-embeddings-convention
Type: note
License: CC BY 4.0 <https://creativecommons.org/licenses/by/4.0/>
Tags: tessera, spatial, zarr, ai, satellite, standards
