Building an iNaturalist Observation Display Tool with a Phone + Claude Code: A Practical Walkthrough

Simon Willison built a zero-maintenance iNaturalist observation display tool using his phone and Claude Code
While camping, Simon Willison built a tool to display iNaturalist nature observations entirely using his phone and Claude Code. The project employs a three-layer serverless architecture: a Python CLI for spatiotemporal clustering, Git Scraping via GitHub Actions for automated JSON data updates, and an AI-generated pure static frontend page for display. The entire solution requires zero maintenance, demonstrating both the viability of AI-assisted mobile programming and the elegance of compositional development.
Project Background
Simon Willison (renowned developer and creator of Datasette) recently shared a fascinating project: while camping, he built a tool to display iNaturalist nature observations entirely using his phone and Claude Code for web. This project demonstrates the practicality of AI-assisted programming on mobile devices, as well as how clever architectural design can chain multiple simple components into a complete data pipeline.
Simon Willison has deep influence in the developer community. He is one of the co-creators of the Django web framework, having participated in Django's early development as far back as 2005. In recent years, he has focused his energy on developing Datasette—an open-source tool that instantly transforms SQLite databases into interactive APIs and web interfaces, widely used in news data analysis, government open data publishing, and similar scenarios. Simon is also one of the most active practitioners and documenters in the AI-assisted programming space, with his blog consistently tracking and reviewing various LLM tools' real-world development performance. His perspectives carry significant weight in the technical community.
iNaturalist is a global nature observation community platform where users can record and share their wildlife and plant sightings. Jointly operated by the California Academy of Sciences and the National Geographic Society, it's one of the most successful projects in the Citizen Science movement. To date, iNaturalist has accumulated over 200 million observation records covering more than 400,000 species, and this data is widely cited by ecology researchers worldwide. The platform provides a comprehensive RESTful API that allows developers to query observation data by user, species, geographic region, and other dimensions—this is the technical foundation that makes Simon's project possible. Simon wanted to aggregate and display observation records from his two different accounts on a single page, sorted by time.
Technical Architecture: Three-Layer Component Design
Layer 1: Python CLI Data Processing Tool
Simon first built a Python command-line tool called inaturalist-clumper for fetching and "clumping" observation records. Its core logic groups observations that are within 2 hours of each other in time and within 5 kilometers in geographic distance into a single clump. This aggregation strategy is highly logical—observations from the same outdoor excursion typically satisfy both conditions.
This spatiotemporal proximity-based clustering method has broad applications in Geographic Information Systems (GIS). The 2-hour time window and 5-kilometer spatial radius correspond well to the spatiotemporal range of a typical outdoor hike or nature observation session. Similar algorithmic thinking appears in products like Google Photos' automatic "travel album" grouping and Strava's activity track identification. In academia, this type of method is considered a simplified version of ST-DBSCAN (Spatiotemporal Density-Based Clustering)—the full ST-DBSCAN algorithm dynamically adjusts density thresholds, while Simon's implementation uses a greedy strategy with fixed thresholds. For personal-scale data, this is more than effective enough while maintaining code simplicity.
Layer 2: Git Scraping for Automated Data Updates
The second step was setting up the simonw/inaturalist-clumps repository, leveraging the "Git Scraping" pattern that Simon himself popularized—using GitHub Actions to run scripts on a schedule and commit the results to a clumps.json file in the repository.
Git Scraping is a data collection pattern that Simon Willison proposed and popularized in 2020. Its core concept is elegantly simple: use GitHub Actions' cron scheduling capability (up to once every 5 minutes) to periodically run a script that fetches data from an external API or webpage, then commit the resulting file via git commit. If the data hasn't changed, Git won't create a new commit; if the data has changed, Git's version history naturally records the timestamp and exact differences of each change. This means you not only get the latest data snapshot but also receive a complete data change history for free. Compared to traditional data pipeline solutions (such as setting up an Airflow scheduler, configuring database storage, and maintaining server uptime), Git Scraping costs virtually nothing—GitHub Actions provides 2,000 free minutes per month for public repositories, more than enough for most scheduled scraping tasks. Hundreds of open-source projects currently use this pattern to track everything from pandemic data to government policy documents.
The elegance of this design lies in the fact that JSON files hosted on GitHub natively support CORS cross-origin requests and can be fetched directly by frontend JavaScript without needing an additional API server.
Specifically, CORS (Cross-Origin Resource Sharing) is a browser security mechanism. By default, JavaScript on a webpage can only request resources from the same origin (same protocol, domain, and port) as the current page. When frontend code needs to fetch data from another domain, the target server must include the Access-Control-Allow-Origin header in its HTTP response to explicitly grant authorization. GitHub's raw.githubusercontent.com domain sets a permissive CORS policy in its response headers, allowing webpages from any origin to request its hosted files. This means you only need to push a JSON file to a public GitHub repository to automatically get a static data API with cross-origin access support—no Nginx configuration needed, no cloud functions to deploy, no API gateway to manage. For personal projects and prototyping, this is an extremely cost-effective solution.
Layer 3: AI-Generated Frontend Display Page
The final step was generating the frontend page. Simon provided Claude Code with a concise prompt:
Build inat-sightings.html—an app that fetches JSON data via fetch(), displays all observations using small.jpg thumbnails (with lazy loading), shows large.jpg in an HTML modal when a thumbnail is clicked, and also displays the common name of the species.
This prompt clearly describes the functional requirements, data source URL, image loading strategy, and interaction details. Claude Code generated a complete single-page application from this specification.
Noteworthy Design Principles
Zero-Maintenance Serverless Architecture
The entire project requires no backend server to run. Data processing is handled by GitHub Actions, data is stored in a Git repository, and the frontend is pure static HTML/JS. This architecture has virtually zero maintenance cost and extremely high reliability.
It's worth noting that the "serverless" here is fundamentally different from commercial serverless platforms like AWS Lambda. Commercial serverless platforms still require you to write and deploy function code, configure triggers, manage permissions, and set up monitoring and alerts—they merely outsource server operations to a cloud provider. Simon's approach is more thorough—the entire system consists of three fully managed components: GitHub Actions (compute), Git repository (storage), and static HTML (presentation). Developers don't need to worry about any infrastructure-level concerns. Even if GitHub Actions occasionally fails to execute, the previously generated JSON file remains available, and the frontend page is unaffected. This inherent fault tolerance is difficult to match with traditional real-time API architectures.
Mobile AI Programming is Already Viable
This project was completed entirely on a phone using Claude Code for web, proving that AI-assisted programming is lowering the barriers to development environments. Developers no longer need to be sitting at a computer to build meaningful tools.
Claude Code is an AI programming tool from Anthropic designed for developers. Its web version allows users to generate, modify, and debug code directly in the browser through natural language conversation. Unlike traditional IDEs (Integrated Development Environments), Claude Code doesn't require users to install any local development toolchain—no Node.js, no Python environment, no package manager. This "zero-configuration" characteristic makes the mobile browser a viable development terminal. Simon's practice here is significant: it demonstrates that AI-assisted programming isn't merely a "copilot" that improves efficiency within existing development environments—it has the potential to redefine the very concept of a "development environment." When prompts become the primary programming interface, full-size keyboards and multiple monitors are no longer necessities, and development can happen in any scenario with a network connection.
Prompt as Specification Document
Simon's prompt writing is worth studying: it includes a specific filename, data source URL, exact image size specifications, performance optimization requirements (lazy loading), and interaction details (modal). This precise prompt enables one-shot generation of usable code.
The lazy loading mentioned here is one of the key techniques in modern web performance optimization. In the traditional image loading model, the browser requests all <img> tag image resources simultaneously during page load. For a page containing hundreds of thumbnails, this means enormous initial bandwidth consumption and lengthy page load times. The loading="lazy" attribute introduced in HTML5 changes this behavior: the browser only loads images within or near the current viewport, progressively loading subsequent images as the user scrolls. This native attribute is now supported by all major browsers without requiring any JavaScript library. For Simon's page displaying numerous nature observation photos, the effect of lazy loading is particularly significant—users might browse only the first few groups of records before leaving the page, in which case hundreds of images that never entered the viewport won't be downloaded at all, greatly saving bandwidth and load time.
Takeaways for Developers
This project embodies a "compositional development" approach: decomposing the problem into three independent layers—data acquisition, data storage, and data presentation—each using the simplest possible technical solution. Git Scraping as a "poor man's data pipeline," combined with GitHub's free hosting and CDN, plus AI-generated frontend code, forms a complete and elegant solution.
This architectural philosophy aligns with the Unix philosophy—"do one thing well, then compose to accomplish complex tasks." Each component is simple enough to be independently tested and replaced: if iNaturalist's API changes in the future, only the Python CLI tool needs modification; if you want a different frontend presentation style, just regenerate the HTML file; if the data source switches from iNaturalist to eBird (another popular bird observation platform), only the data fetching layer needs replacement while the storage and presentation layers remain completely unaffected. This loosely coupled design keeps long-term maintenance costs extremely low.
For developers looking to build personal data display projects, this pattern is highly replicable—whether tracking exercise records, reading data, or other personal data accessible via API, a similar architecture can be adopted. For example, you could use the same pattern to periodically scrape Goodreads reading history, Last.fm listening history, GitHub's own contribution data, or even weather API historical data, then visualize it through an AI-generated static page. The total cost of the entire process is zero—the only investment is your creativity and a carefully crafted prompt.
Key Takeaways
- Simon Willison built an iNaturalist observation display tool entirely using his phone and Claude Code while camping
- The project uses a three-layer serverless architecture: Python CLI data processing + Git Scraping auto-updates + pure static frontend display
- JSON data hosted in a GitHub repository natively supports CORS cross-origin access, eliminating the need for a separate API server
- Through precise prompt description, Claude Code generated a complete frontend page with lazy loading and modal interactions in a single pass
- The Git Scraping pattern provides a zero-cost, highly reliable data pipeline solution for personal data projects
Related articles
TutorialsChatGPT Plus Subscription Guide: Are GPT-5.5, image-2, and Codex Worth the Upgrade?
A detailed look at ChatGPT Plus features — GPT-5.5, image-2, and Codex — with a Plus vs Pro comparison and a complete step-by-step subscription guide for users outside the US.
TutorialsHarness AI Engineering in Practice: Using Claude Code to Master Enterprise-Level E-Commerce Development
Deep dive into Harness AI Engineering: master enterprise e-commerce development with Claude Code using the Rules, Skills, Wiki, and Changes framework.
TutorialsCursor + Codex Dual-IDE Collaboration: A Practical Methodology for Open-Source Project Customization
A complete methodology for open-source project customization based on real-world experience, detailing the Cursor+Codex dual-IDE workflow, seven-stage process, MVP validation, and AI source code reading techniques.