Case study, 10–11 November 2026. Built with Claude Code on two machines: axon, a Pixelbook running Omarchy, and vortex, a Windows box with an RTX 2080 Super. About eleven hours of wall clock, most of them while I was asleep.
I read an article, followed its footnote, and lost a night to it. This is how Claude and I went from “explain how lidar works” to locating a hidden Maya hilltop in public NASA data and rebuilding it in 3D, and what the whole exercise taught me.
A footnote in someone else’s discovery
On 10 November, Pietro Miotti published “My AI Agents Found an Unmapped Maya Site”. It is a four-day diary: a thought while travelling in Peru, an overnight run of 48 agents looking at NASA G-LiHT lidar strips, a hilltop with a plaza, an oval pyramid and a raised courtyard under unbroken forest, and an official reply from INAH, Mexico’s National Institute of Anthropology and History, saying no registered site existed at that location. He kept the coordinates in a confidential annex and blacked out the state and municipality in the letters.
The article ends with a footnote: if you don’t know how lidar works, paste this into your favourite AI tool: “Build an HTML page that explains how lidar works, with beautiful visualizations.”
So I did, in a Claude Code session on my Pixelbook. Twenty minutes later I had an interactive explainer: a flight animation dropping pulses through a forest, a time-of-flight calculator with G-LiHT’s real altitude and pulse rate, the multiple-returns waveform, a point-cloud slice with vegetation and ground toggles, and a synthetic hilltop you can light, exaggerate and label. That was supposed to be the whole evening.
Then I asked the question that turned it into a project.
”How hard would it be to reverse-engineer the location?”
Claude’s answer was: not very, and that is the uncomfortable part. The author withheld the coordinates, but everything else was public. The dataset is open. The article names the instrument, the year and the region. It publishes a hillshade of the hilltop, a zoom of the civic centre with a scale bar, two terrain cross-sections, a 3D turntable and a satellite slider. A plaza with an oval mound on a cliff edge is a distinctive shape, and cliffs are rare in the lowlands. Confidentiality rested on nobody bothering, not on the information being hidden.
I wanted to know whether it could actually be done. Not to go there, not to tell anyone, just curiosity about how thin the veil really was. Before a single byte was downloaded, Claude set a condition and I agreed to it. It held for the whole project.
The rules. The coordinates go into one local file on one machine. They are never synced, committed, posted, put into a published page, or narrowed down in conversation beyond what the article itself says. Every image and model that leaves the machine is in local metres, with no coordinates, tile names or flight-line identifiers. Mexican archaeological sites are protected by federal law, INAH has the report, and the biggest threat to an unexcavated site is a pin on a public map.
This post follows the same rules. Nothing here narrows the location beyond “a hilltop on a cliff in the Maya region of Mexico, crossed by a 2013 NASA flight line”, which is what the article already says.
Eighty minutes from first download to a match
The plan Claude proposed was a template match: take the civic-centre crop from the article, bring it to the data’s scale, and slide it across every bare-earth terrain tile the campaign produced. Miotti’s agents had looked at pictures; we would let OpenCV do the looking. I created the NASA Earthdata token the article mentions while Claude checked the page for leaks and started the downloads.
Why the first two passes found nothing
The first run rendered every tile as a conventional hillshade, lit from the north-west, and threw away any window containing no-data pixels. It ranked a flat interpolated strip edge first. Claude went back to the article’s pyramid crop, enlarged it four times and saw what the agents’ renderer had actually done: slope shading, dark on every steep edge regardless of direction, with the strip’s no-data painted white. And that white edge runs right through the civic-centre box, so the “reject any no-data” rule would have discarded the true window even with the right rendering.



When the third pass finished, Claude told me it had found it, gave me the score and the path to the file, and nothing else. I opened the crop next to the article’s figure. Feature for feature.
- Match the renderer, not the terrain. Template matching compares pictures. Until the tiles were rendered the way the article’s figure was rendered, the true window could not score.
- Treat no-data as signal. The strip edge is part of the fingerprint. A high-pass filter on both sides made the plaza and pyramid edges drive the score instead of hillside tone.
- Distrust clever shortcuts. A filter on flight heading, measured from the figure, would have excluded the correct tile: the tile’s principal axis did not represent the local strip direction. A slower rule-based terrain detector was started and abandoned. Brute force over 520 tiles took 15 minutes and was right.
- No token was needed. The terrain tarballs turned out to be public. I created the Earthdata token and we never used it.
One ribbon of data, 450 m wide
My next question was whether adjacent tiles could be stitched into a larger map. They could, but only lengthwise. The 2013 survey was a forest-carbon sampling mission flown as long transects, never as area coverage. Within 37 km of the site there is exactly one flight line, about 450 m wide, in any Mesoamerican G-LiHT campaign. Everything we built sits on that ribbon, and everything beyond its edges in the renders is invented smooth ground.


That led to a remark of mine that the find was sheer luck, and to a correction I keep thinking about. Miotti’s 1,200 km² of strips is well under 1% of the Maya lowlands, so for any one hilltop the odds of a transect crossing it are tiny. But the Classic Maya lowlands were one of the most densely settled pre-industrial landscapes anywhere, so a 1,200 km² transect through forest is almost guaranteed to cross hundreds of mounds. The lottery was never whether the laser would find ruins. It was whether the ruins under the beam were already on a map. Hold that thought until the end.
A 3D viewer with the real forest on top
Claude turned the merged terrain into a self-contained Three.js page: a 1 m heightmap packed into the HTML, orbit and pan, slope or height or clay textures, vertical exaggeration, a movable sun, and a canopy layer built from 2 million vegetation returns in the raw point cloud. Switch the canopy on and the ruins vanish under 10–17 m of trees, which is the article’s whole point. The page uses local metres only, so it carries no coordinates.



Painting a photograph onto real relief
The laptop has no image generation, so I offered vortex, my Windows box with an RTX 2080 Super. Claude’s idea was not to ask a model to imagine a Maya site, which produces generic temple art, but to force it to follow the actual terrain. The viewer gained a depth-map output and a normal-map output from any camera, with the strip’s no-data filled by a smooth membrane so there is ground everywhere in frame. That depth map became the control image for Stable Diffusion XL with a depth ControlNet, running in ComfyUI on vortex and driven over an ssh port forward from the laptop. My part was closing a few dozen browser tabs so the GPU had its memory back.





What failed, so nobody repeats it


- Close cameras only. In wide views the civic centre is too small to matter and the model paints a generic landscape.
- Fill the no-data first. Holes in the depth map become holes in the world.
- A photoreal fine-tune over the base model, no “thatched”, and the camera above the exaggerated surface. Twice the camera ended up underground because of the 1.5× exaggeration.
A Blender scene from the heightmap
I asked what the plan for Blender would be, and the answer was a good one. AI stills are one-off paintings; nothing in them is a consistent object, so a video from them would flicker. The opposite route is a real scene: coherent, but only as good as what you build. A portable Blender 5.2.4 went onto both machines and one Python script builds the whole thing headless from three rasters in local metres: the heightmap, a canopy height model from the point cloud, and a mask of the mounds inside the civic-centre box. I said go and went to do something else.
- Terrain mesh at 1 m, 1.8 million faces, with a 30 km skirt so there is a horizon. Procedural rock on slopes, soil, grass on cleared ground, lime plaster on the mounds.
- Forest instanced by geometry nodes: density and scale from the 2013 canopy heights, so the trees are tall where the lidar says tall, and absent where we choose to clear.
- Physical sky with the sun in it, distance haze mixed into every material, 1.3× vertical exaggeration.
- Three modes from one script: today (the hilltop under its real forest), bare (the lidar surface as clay) and recon (terraced plastered mounds, cleared plaza, forest beyond).

today recon 

The first flythrough attempts on the laptop went black after a few frames: Eevee under a software OpenGL driver is not a renderer, it is a screenshot tool. Everything that moved was rendered on the GPU box, with the laptop keeping an ssh session open because Windows kills any process started over ssh the moment the session ends, whether through Start-Process or the task scheduler. The one real stall of the night was mine: my 1Password ssh agent sat waiting for an Allow click for about an hour while I was away from the laptop.
Buildings on the footprints
I saw the videos and asked for one with more detailed architecture, something closer to the AI stills. Claude laid out what it would build and I said: go with the pyramids. The recon mode read as white blobs rather than buildings, and the scene already knew where the mounds were, so a detector segmented them: mounds are what stands more than 1.2 m above an opened version of the surface, platforms are broader rises, and the pyramid is the component with a body more than 3 m tall, with an ellipse fitted to it. A second pass split the courtyard mound that the first detection had merged into the pyramid. The plaza floor is the minimum-area rectangle of the flat cells at plaza level, and the terrain is flattened underneath it.

- Pyramid: stepped oval, five terraces fitted to the mound body, red base band, stairway facing the plaza, two-room temple with a roof comb on top.
- Plaza: plastered rectangular slab, stela and altar in front of the stairs.
- Range buildings with three doorways on footprints over 20 m, including the courtyard the article reads as the ruler’s residence.
- Pole-and-thatch houses with hipped roofs on the small platforms.
- Six tree types: three broadleaf, two flat-crowned emergents and a palm, with the forest edge cleared around the group.



Where it still falls short of the AI stills: surfaces are flat colour with light weathering, no carved detail, no people or smoke, and the trees are stylised. It reads as a museum reconstruction model, not a photograph. The next step up is real tree and texture assets, which is a different project.
Installs, disk and time
Nothing was installed into either operating system. Every tool is a portable unpack, a project virtualenv or a mise-managed binary, and deleting two folders removes all of it. I asked for the plan before each install and approved it; Claude did the rest.
| Machine | What | Where | Size |
|---|---|---|---|
| axon | Python venv: numpy, rasterio, OpenCV, scipy, laspy, Pillow | tools/.venv | small |
| axon | Node 26.13 and uv 0.13 (side effect of a syntax check and the venv) | mise store | small |
| axon | Blender 5.2.4 portable, Linux | tools/blender/ | 1.6 GB |
| axon | G-LiHT tarballs, extracted terrain tiles and point-cloud tiles (deleted after) | data/gliht/ | 33 GB |
| axon | Mosaics, viewer, renders, scene inputs and outputs | data/mosaic/ | 357 MB |
| vortex | ComfyUI portable + SDXL base, RealVisXL V5, xinsir depth ControlNet, VAE | D:\lidar\ | ≈ 21 GB |
| vortex | Blender 5.2.4 portable, Windows, plus the scene package and renders | D:\lidar\ | ≈ 5 GB |
Scripts, in the order they were written
| Script | Does |
|---|---|
fetch.sh / process.sh | Download the public terrain tarballs per flight set, extract, hand tiles to the matcher as they land. |
match.py | Template from the article figure at 1.36 px/m; render tiles as slope shading with white no-data; high-pass; normalised cross-correlation; peaks to a local CSV. Prints scores only. |
verify.py | Hillshade crop and west–east / south–north profiles for the top candidates. |
heading.py, detector.py | The strip-heading filter and rule-based terrain detector. Both abandoned; both would have misled. |
mosaic.py | Merge every tile within range of the site; slope, hillshade and height renders at 1 m and a 5 m strip overview. |
canopy.py | Vegetation returns from the compressed point-cloud tiles. |
export3d.py + viewer.tpl.html | Self-contained Three.js viewer with depth and normal render modes and membrane no-data fill. |
comfy_render.py | Submits SDXL + depth-ControlNet jobs to ComfyUI’s API through an ssh tunnel. |
blender_inputs.py | Heightmaps, canopy, masks and the structure footprints as PNGs and JSON in local metres. |
build_scene.py + arch.py | Headless Blender scene builder: terrain, forest, sky, four modes, eight cameras, flythroughs and orbit; the Classic-Maya massing. |
Timeline, local time
Tuesday 10 November
17:09Lidar explainer page published, twenty minutes after I pasted the footnote.
17:15I ask how hard it would be. Answer: an afternoon for someone who scripts it.
17:32I say let’s do it. Leak check of the article: nothing. Downloads begin, matching starts as each set lands.
18:23First two passes miss. The article’s images are recognised as slope-shaded; renderer rewritten.
18:40Match confirmed, 0.74 against 0.40. Coordinates written to one file, permissions 600.
20:05Mosaic built. Only one strip exists; nearest other flight line 37 km away.
20:21Interactive 3D viewer with the real canopy.
20:28I approve the vortex plan; ComfyUI and 16 GB of models land on the D drive. I close my browsers to free the GPU.
21:12AI renderings done. Vortex idle.
21:21I approve the Blender plan and leave. Scene builder written and iterated on the laptop.
22:08Blocked for an hour on my 1Password ssh agent waiting for an Allow click.
23:55GPU stills and two flythroughs back from vortex.
Wednesday 11 November
00:00”Go with the pyramids.” Architecture mode built: footprints, massing, better trees.
02:37First orbit video reviewed: camera sinks into the forest. Re-rendered at constant height.
04:21Final orbit video copied back. Everything idle. I was asleep.
Nine things worth keeping
- Withholding coordinates is not protection when the dataset is public. A distinctive shape in a published figure plus an open archive is a solved problem. Anyone publishing a find should assume the figure is the location.
- The hard part was looking at the pictures correctly. Three passes, and the only thing that changed between failure and a 0.74 match was rendering the data the way the target image had been rendered.
- Brute force beat cleverness every time. The heading filter and the terrain-rule detector were both elegant and both wrong. Sliding a template over 520 tiles took 15 minutes.
- Sampling lidar finds things because the ground is crowded, not because the plane was lucky. The lottery is whether what it finds is already registered.
- Depth-conditioned image models are a terrain-faithful rendering tool, as long as the camera is close, the depth is hole-free and the prompt avoids words the model over-reads.
- A real scene beats a pretty painting the moment you need a second frame. Coherence costs modelling time; the AI stills cost 47 seconds each and could never be animated.
- Portable everything. No system installs on either machine, and the clean-up is two folders.
- Windows over ssh forgets you the moment you hang up. Keep the session open from the other end; nothing else survived.
- An agent can run a two-machine pipeline while I sleep, but it needs a stated boundary before it starts. The privacy rule was set once, at the beginning, and never had to be renegotiated. My job was mostly saying “go”, closing browser tabs, and clicking Allow.
What happened when it went out
I posted the AI still of the reconstruction as a reply to the article’s author on X. It reached about 14,000 views, 150 likes and 5 reposts, and brought in more followers in a day than the previous month had. I then gave the same still to Grok Imagine, which produced a fly-by video from the single image that almost looks real.
@PietroMiotti Artist's rendition of what this could've looked like, based on the data.
— Carlos Granier (@cgranier) November 11, 2026
A community note on the post said the hilltop had been found before. On 11 November, Miotti confirmed it: the site appears in a 2025 paper by Estrada-Belli, Canuto, Šprajc and Fernandez-Diaz, “New regional-scale Classic Maya population density estimates and settlement distribution models through airborne lidar scanning”, on page 14. That is the same paper his article had cited for its population estimates. The site was in the literature all along, just not in INAH’s register, and INAH’s reply was accurate about the register.
I read the paper. It is open access, and it did something the article did not expect: the authors took 500 km² of the same 2013 environmental lidar, reprocessed it for structure visibility, and digitised 16,206 structures in it. Page 14 is their Figure 13, the four largest “minor ceremonial centres” in the whole G-LiHT dataset, with plaza areas between 8,000 and 13,000 m². One of the four panels is our hilltop, with the plaza platform, the structures and the hollow already drawn as a reservoir, and a dot on an inset map of the transects. The agents found the most prominent thing in the data. So had the archaeologists, two years earlier, and they had ranked it.
Remember the lottery. The laser was always going to find ruins; the only question was whether someone had already written them down. Someone had. That changes the article’s headline and none of the method, and it changes nothing about how easy the location was to recover from a public figure. If anything it makes the lesson sharper: the first check after “is this in the register?” should have been “is this in the papers I am citing?”, down to the figures, and that check is one an agent can run before the report goes out. The paper’s figure is not reproduced here for the same reason the article’s are not.
What this post deliberately leaves out. No coordinates, no tile names, no flight-line identifiers, no state or municipality. Every render is in local metres. The location file, the georeferenced mosaic and the results stay on one laptop and nowhere else; the 33 GB of tiles have already been deleted. The article’s own figures are not reproduced here; go read it.
ComfyUI and Blender stay on the GPU box. The scripts stay. The next time someone publishes a hilltop, the pipeline is an afternoon away, and so, apparently, is the paper that already describes it.
Sources. Pietro Miotti, “My AI Agents Found an Unmapped Maya Site”. Lidar data: NASA Goddard’s G-LiHT programme, 2013 campaigns, public bare-earth terrain and point-cloud products. Prior documentation: Estrada-Belli, Canuto, Šprajc and Fernandez-Diaz, Journal of Archaeological Science: Reports 66 (2025) 105288. Rendering: Three.js, ComfyUI with Stable Diffusion XL and a depth ControlNet, Blender 5.2.4 Cycles. All images in this post were produced during the project; none carry georeferencing.
New posts by email
New posts and case studies, sent when they're published. Unsubscribe anytime.
About the Author
Carlos Granier is a Tech Founder, CTO, and AI Strategist with 25 years of experience building at the intersection of technology and business. He co-founded Pongalo, one of the first US Hispanic OTT platforms, and built a YouTube MCN to 200M+ monthly views. He now helps founders and executives implement AI as practical infrastructure. Based in Miami, Florida.
Let's Connect
If you want to work together , or just say hi, email me at blog-inbound@cgranier.com or reach out on any of these:
- X (Twitter) /
- Threads /
- Instagram /
- GitHub /