A Val Town app that visualizes Helium Mobile hotspot growth in the DC Metro area over time, featuring an interactive deck.gl map with H3-based clustering (rendered as scaled circles) and timeline scrubbing.
import sqlite from "https://esm.town/v/std/sqlite@14-main/main.ts"index.ts - Main Hono server with API routesbackend/db.ts - SQLite schema and queriesbackend/heliumApi.ts - Helium Entity API clientbackend/scraper.ts - Backfill and daily scrape logicbackend/migrate.ts - Migration script between SQLite versionscron.ts - Daily scraper cron job (6 AM UTC)frontend/components/App.tsx - Main React component with map, timeline, and clusteringfrontend/utils/animalHash.ts - Animal name generator (angry-purple-tiger algorithm)frontend/style.css - Helium-inspired dark theme styles~/.deno/bin/vt push~/.deno/bin/vt browse<script src="https://unpkg.com/deck.gl@8.9.33/dist.min.js"></script>deck object (esm.sh imports have luma.gl compatibility issues)radiusUnits: "pixels", stroked: falseradius = 8 + 27 * (sqrt(count-1) / sqrt(69))transitions: { getRadius: { duration: 150 }, getFillColor: { duration: 150 } }fontFamily: "Figtree, sans-serif", fontWeight: 300https://basemaps.cartocdn.com/dark_all/{z}/{x}/{y}@2x.pngid: isPlaying ? `hotspot-clusters-${selectedIdx}` : "hotspot-clusters"
transitions: undefined during playback (not { duration: 0 } - that still triggers interpolation)pickable: !isPlayingflex: 1 1 0, min-width: 0 (allows fractional pixel widths)min-width: 0 prevents overflow on narrow viewports with many barscalc(12px + (100% - 24px) * ${(currentIdx + 0.5) / snapshots.length})transform: translateX(-50%) to center the 2px line on calculated positiononTouchStart, onTouchMove, onTouchEnd handlers mirror mouse eventsselectedIdx) instead of date string to avoid linear searchesclustersByDate Map computed once on data load for O(1) lookuptransitions: undefined (not duration: 0)pickable: !isPlaying for minor performance gainheight: 100dvh (dynamic viewport height) instead of 100vhviewport-fit=cover to viewport meta tagenv(safe-area-inset-bottom) for timeline padding--bg-primary: #04081bbackground: var(--bg-card), backdrop-filter: blur(15px), border: 1px solid var(--border-color), border-radius: 12pxpadding-left: 3px for triangle (reset to 0 for pause icon)GET /api/hotspots - All active hotspotsGET /api/snapshots - Daily snapshot statsGET /api/stats - Current count and latest snapshotcron.ts runs daily at 6 AM UTC via Val Town's cron scheduler@14-main vs unversioned) use isolated database instancesbackend/migrate.ts) copies data between database instancesconst DC_BOUNDS = {
minLat: 38.5, maxLat: 39.3,
minLong: -77.5, maxLong: -76.7
};