Skip to content
Back to projects

Professional work

Anvil Labs

  • Typescript
  • React
  • CesiumJS
  • ThreeJS
  • AWS
  • Python
  • Django

Anvil Labs is an industrial 3D drone platform that turns raw drone imagery into models teams can work on together. Users can upload photos, process them into detailed 3D models, take measurements, plan work, inspect sites, and generate reports without jumping between tools.

For sites scanned more than once, users can compare models and track changes over time. That makes it useful for construction sites, industrial facilities, and infrastructure projects where understanding what changed matters as much as seeing what exists today.

My role

I joined as a frontend developer, working mainly on the React application. As the project grew and the client became more confident in my work, I took on more responsibility across the stack, including the Python backend and AWS infrastructure for storage and processing. I got to work on both how people used the product and how it handled their data behind the scenes.

A workspace that fits the user

Some users just wanted to open a model, take a few measurements, and move on. Others needed several views, tools, and datasets open at once. A fixed dashboard would suit one group and get in the way of the other.

I built a tabbed, window-style layout that let users arrange the application around their work. A simple setup could be one model filling the screen; a more involved workflow could use several tabs and panels side by side.

Single window layout
Custom multi window layout

The goal was simple: Start easy, but don’t create a ceiling for power users.

Choosing CesiumJS for the 3D work

We considered building more of the 3D and geospatial functionality ourselves with Three.js. But cameras, coordinates, terrain, and large geospatial scenes would have taken considerable time to get right. At that stage, the client wanted a strong product in users’ hands quickly.

I chose CesiumJS because it already covered that foundation. It let us spend more time on the tools customers would use, while avoiding custom work that wouldn’t give the product a meaningful advantage.

Buy back development time where a custom solution would not give us a meaningful advantage.

Storing and processing large drone datasets

A single drone project can contain thousands of high-resolution images, videos, models, and generated files. Many scans cover private properties or industrial sites, so we needed reliable storage and control over who could access it. AWS S3 became the storage layer for both the original uploads and the processing output.

Turning those images into a 3D model can take hours or days. We used EC2 and AWS Batch for the heavy processing, with Step Functions coordinating the stages. Processing machines read input directly from S3 and wrote completed models back to it. Keeping storage and compute in the same system saved us from moving huge datasets between unrelated services.

Understanding failures before retrying them

A job failing at 95% after two days wastes time and compute, and someone still has to figure out what happened.

Because of that,

observability became a core part of the pipeline rather than something we added later.

We built an internal dashboard to show where each job was in the pipeline and help the team investigate failures. A status like failed wasn’t enough; we needed to understand what had stopped before starting another expensive run.

The dashboard also let support handle common problems without calling in an engineer. They could retry failed jobs with one click and raise or lower a job’s priority when a customer needed urgent help.

The goal was to make the pipeline operable, not just functional.

Keeping a manual recovery option

Photogrammetry is sensitive to lighting, image overlap, camera settings, terrain, and many other variables. Checks, retries, and sensible defaults helped, but we couldn’t realistically automate every possible failure.

As a last resort, we modified the EC2 processing images to allow remote access through VNC. A photogrammetry expert could inspect a stuck job, adjust processing parameters or outputs, and continue it without needing a developer. Because these machines handled private customer data, access was tightly controlled and enabled per request, only after the usual recovery options had failed.

We automated the common cases, added visibility for the uncommon ones, and kept a controlled human fallback for the cases software could not safely predict.

Learning from real usage

I integrated PostHog to see how customers moved through the application, which features they used, and where they got stuck. We found people combining tools in ways we hadn’t planned for. Seeing those workflows gave us much more to work with than guessing what users wanted from inside the development team.

The integration also supported A/B testing, campaigns, and trying changes before a wider rollout. It gave us a way to follow up on decisions after they shipped and see whether they actually helped.

Helping users inside the application

We also brought support into the product. With explicit consent, support could temporarily join and take over a user’s session to help them directly. For complex 3D tools, seeing the exact state of the application was much easier than piecing together screenshots and email descriptions. Access stayed under the user’s control.

building a good interface is only half the job. You also need a way to see how people actually use it.

Importing from Google Drive and Dropbox

Customers often already had hundreds or thousands of drone files in Google Drive or Dropbox. Making them download everything and upload it again would have been slow and frustrating.

I built an adapter that accepted a shared link and copied the files directly into the project’s S3 storage. Once there, they followed the same processing path as a normal upload. Customers avoided the extra transfer, and the rest of the backend didn’t need separate logic for each source.

Building the glue around the product

There were dozens of small, recurring jobs around Anvil Labs that needed to run reliably. A contact request, for example, needed more than an email notification: we wanted to understand the request, prepare a summary, schedule a follow-up on the client’s calendar, and notify the sender. Feedback needed to become Linear tickets when actionable, and Slack needed updates about sign-ups and large processing jobs.

I set up a self-hosted n8n instance to handle these workflows. It connected the product to email, calendars, Slack, Linear, and external data sources without filling the backend with custom integrations. Changing a notification or adding a step to a workflow could happen without touching the main application.

We eventually used it for some smaller product features too. The in-app news feed needed to collect content from several sources, clean it up, and make it available to the application. n8n already handled that well, and we could add or replace sources without building a separate service each time.

if something needed to work, but didn’t need to become part of the product’s core architecture, n8n was often the fastest and cleanest place for it.

What I took away

Anvil Labs took me from frontend work into backend development, cloud infrastructure, and the practical work of keeping long-running jobs reliable. It also changed how I approached product decisions: watching customers use the application often taught us things we couldn’t see while building it.

Good engineering is not about building everything yourself. It is about knowing what deserves a custom solution and what should be solved with a tool that already does the job well.

You cannot fully understand a product from the development side. Watching how customers actually use it will always teach you something.

Not every problem deserves another service or another feature. Sometimes the right answer is a small piece of automation connecting the systems you already have.

And when your system is doing expensive work that can run for days:

Being able to see, debug, recover, and occasionally intervene in it is part of the feature - not an extra.

Explore more projects