• Blog
  • Docs
  • Pricing
  • We’re hiring!
Log inSign up
jhiller

jhiller

dc-hotspot-map

Map tracking Helium Hotspot growth around Washington DC
Public
Like
dc-hotspot-map
Home
Code
7
backend
3
frontend
6
shared
1
CLAUDE.md
README.md
C
cron.ts
H
index.ts
Branches
1
Pull requests
Remixes
History
Environment variables
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.
Sign up now
Code
/
CLAUDE.md
Code
/
CLAUDE.md
Search
…
Viewing readonly version of main branch: v101
View latest version
CLAUDE.md

DC Hotspot Growth Map

Project Overview

A Val Town app that visualizes Helium Mobile hotspot growth in the DC Metro area over time, featuring an interactive Leaflet map with H3-based clustering (rendered as scaled circles) and timeline scrubbing.

Deployment

  • Live URL: https://jhiller--10367c64f7d511f0990e42dde27851f2.web.val.run

Tech Stack

  • Platform: Val Town (Deno runtime)
  • Backend: Hono HTTP framework
  • Database: Val Town SQLite
  • Frontend: React + deck.gl (WebGL)
  • Clustering: Uber H3 (resolution 8) for grouping
  • Map Tiles: CartoDB dark-matter (via TileLayer)
  • Styling: Helium-inspired dark theme

Key Files

  • index.ts - Main Hono server with API routes
  • backend/db.ts - SQLite schema and queries
  • backend/heliumApi.ts - Helium Entity API client
  • backend/scraper.ts - Backfill and daily scrape logic
  • cron.ts - Daily scraper cron job (6 AM UTC)
  • frontend/components/App.tsx - Main React component with map, timeline, and clustering
  • frontend/utils/animalHash.ts - Animal name generator (angry-purple-tiger algorithm)
  • frontend/style.css - Helium-inspired dark theme styles

Val Town CLI

  • Push changes: ~/.deno/bin/vt push
  • Open in browser: ~/.deno/bin/vt browse
  • Configure cron schedule in Val Town web interface

Design Decisions

deck.gl Visualization

  • Using ScatterplotLayer with radiusUnits: "pixels"
  • Circles scaled by count using sqrt easing curve: radius = 8 + 27 * (sqrt(count-1) / sqrt(69))
  • Min radius: 8px (count=1), Max radius: 35px (count=70)
  • Easing makes 1→2 dramatic but 67→70 nearly imperceptible
  • Built-in transitions: transitions: { getRadius: { duration: 150 }, getFillColor: { duration: 150 } }
  • TileLayer for CartoDB dark basemap tiles
  • Stroke: 1px width, 0.15 opacity (white)

Animal Names (animalHash.ts)

  • Uses exact angry-purple-tiger algorithm with blueimp-md5
  • Critical: ADJECTIVES array must include duplicate "skinny" at index 85 (after "shallow")
  • Display names in Title Case (e.g., "Angry Purple Tiger"), not kebab-case

Timeline/Histogram

  • Histogram bars must have gap: 0 to align with slider (gaps accumulate across ~1000 bars)
  • Histogram height: 40px desktop, 30px mobile
  • Histogram capped at 10 new hotspots to prevent outliers from flattening
  • Playback at 15ms interval (~67 fps)
  • When at end of timeline and user presses play, reset to beginning

iOS Safari Viewport Fix

  • Use height: 100dvh (dynamic viewport height) instead of 100vh
  • Add viewport-fit=cover to viewport meta tag
  • Use env(safe-area-inset-bottom) for timeline padding

Visual Design (Helium-inspired)

  • Dark theme with --bg-primary: #04081b
  • Accent colors: cyan (#4FC3F7), green (#00d97d), purple (#5e25fd)
  • Avoid bold fonts - use font-weight 400-500
  • Circle fill opacity: 0.5 for established (grey), 0.7 for new/recent
  • Map zoom control hidden (zoomControl: false)

Color Coding (Hotspot Age)

  • Green (#2ecc71): New (< 7 days)
  • Blue (#3498db): Recent (< 30 days)
  • Grey (#a0a0a0): Established (30+ days)

Mobile Responsive (max-width: 768px)

  • Timeline padding: 10px 12px with safe-area-inset-bottom
  • Histogram height: 30px
  • Play button: 36x36px
  • Legend hidden on mobile

API Endpoints

  • GET /api/hotspots - All active hotspots
  • GET /api/snapshots - Daily snapshot stats
  • GET /api/stats - Current count and latest snapshot
  • POST /api/admin/backfill - Trigger data backfill
  • POST /api/admin/scrape - Trigger daily scrape

DC Metro Bounding Box

const DC_BOUNDS = { minLat: 38.5, maxLat: 39.3, minLong: -77.5, maxLong: -76.7 };
FeaturesVersion controlCode intelligenceCLIMCP
Use cases
TeamsAI agentsSlackGTM
DocsShowcaseTemplatesNewestTrendingAPI examplesNPM packages
PricingNewsletterBlogAboutCareers
We’re hiring!
Brandhi@val.townStatus
X (Twitter)
Discord community
GitHub discussions
YouTube channel
Bluesky
Open Source Pledge
Terms of usePrivacy policyAbuse contact
© 2026 Val Town, Inc.