Public
Like
dc-hotspot-map
Val Town is a collaborative website to build and scale JavaScript apps.
Deploy APIs, crons, & store data – all from the browser, and deployed in milliseconds.
Viewing readonly version of main branch: v78View latest version
A Val Town app that visualizes Helium Mobile hotspot growth in the DC Metro area over time, featuring an interactive Leaflet map with H3 hexagonal clustering and timeline scrubbing.
- Platform: Val Town (Deno runtime)
- Backend: Hono HTTP framework
- Database: Val Town SQLite
- Frontend: React + Leaflet.js
- Clustering: Uber H3 (resolution 8, ~0.74 km² hexagons)
- Map Tiles: CartoDB dark-matter
- Styling: Helium-inspired dark theme
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 logicfrontend/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
- Push changes:
~/.deno/bin/vt push - The app auto-deploys on push
- Open in browser:
~/.deno/bin/vt browse
- File:
cron.ts- Daily scraper that fetches new hotspots - Schedule:
0 6 * * *(6 AM UTC daily) - Configure schedule in Val Town web interface: https://www.val.town/x/jhiller/dc-hotspot-map
- The cron calls
runDailyScrape()which:- Fetches all DC area hotspots from Helium API
- Inserts new hotspots with created_at date
- Tracks location changes
- Updates daily snapshots
- Uses exact angry-purple-tiger algorithm with blueimp-md5
- Critical: ADJECTIVES array must include duplicate "skinny" at index 85 (after "shallow") to match original library
- Display names in Title Case (e.g., "Angry Purple Tiger"), not kebab-case
- Resolution 8 provides good balance of detail vs. clustering
- Hexagons colored by newest hotspot age in cluster
- Count labels displayed in center of each hexagon
- Histogram bars must have
gap: 0to align with slider (gaps accumulate across ~1000 bars) - Histogram capped at 10 new hotspots to prevent outliers from flattening visualization
- Playback at 100ms interval (10 fps)
- When at end of timeline and user presses play, reset to beginning
- Dark theme with
--bg-primary: #04081b - Accent colors: cyan (#4FC3F7), green (#00d97d), purple (#5e25fd)
- Avoid bold fonts - use font-weight 400-500
- Hex fill opacity: 0.35 for established (grey), 0.5 for new/recent
- Hex border opacity: 0.2
- Map zoom control hidden (
zoomControl: falsein Leaflet options)
- Green (#2ecc71): New (< 7 days)
- Blue (#3498db): Recent (< 30 days)
- Grey (#a0a0a0): Established (30+ days)
- Timeline padding reduced to 12px
- Histogram height reduced to 30px (from 50px)
- Play button: 36x36px (from 48x48px)
- Slider thumb: 14px (from 18px)
- Timeline labels: 10px font
- Legend hidden on mobile
- Stats panel font size reduced
GET /api/hotspots- All active hotspotsGET /api/snapshots- Daily snapshot statsGET /api/stats- Current count and latest snapshotPOST /api/admin/backfill- Trigger data backfillPOST /api/admin/scrape- Trigger daily scrapePOST /api/admin/compute-snapshots- Recompute historical snapshotsPOST /api/admin/clear-and-backfill- Clear DB and re-backfill
const DC_BOUNDS = {
minLat: 38.5, maxLat: 39.3,
minLong: -77.5, maxLong: -76.7
};