Skip to content

Malgsx/Bitchat-Heatmap

Repository files navigation

BitChat Heatmap

Real‑time heatmap and chat explorer for Nostr geohash activity (kind 20000). The app ingests public relay traffic over WebSocket, aggregates activity by geohash, and visualizes hotspots on a Leaflet map with a Discord‑style channel sidebar and a searchable chat timeline.

Highlights

  • Dual/Chat/Map layouts with a single click view switcher (click the same view again to revert to your previous view)
  • Always‑visible, modern, fixed‑width sidebar in Dual view (collapses automatically in Chat/Map)
  • Vibrant heatmap with numeric badges showing unique users per geohash (not message count)
  • Click a heat badge to jump to that geohash channel and focus the map
  • Chat timeline with pause and auto‑scroll controls, and an inline search bar (🔍) above the general header
  • Resilient WebSocket client with reconnection and optional initial cache fetch
  • Production build served by a Node/Express server that also handles a WebSocket fan‑out and REST API

Architecture

  • Frontend: Vite + React 18 + TypeScript
  • Map: Leaflet + CartoDB dark tiles + leaflet.heat
  • Backend: Node.js + Express + ws WebSocket server
  • Data: Nostr relays (configurable) with in‑memory cache (node-cache)
client (vite dev)  <—ws/http—>  server (express + ws)
   ↳ React app renders map + chat         ↳ connects to relays, caches, broadcasts

Repository layout

/ (project root)
├─ server.js                 # Express + ws server, REST API, Nostr relay ingestion
├─ src/
│  ├─ components/
│  │  ├─ ChannelSidebar.tsx  # Modern sidebar (geohashes as channels)
│  │  ├─ ChatTimeline.tsx    # Timeline with pause/autoscroll + 🔍 search
│  │  └─ HeatmapView.tsx     # Leaflet map + heat layer + badges
│  ├─ hooks/
│  │  └─ useWebSocket.ts     # Client WebSocket lifecycle + reconnection
│  ├─ types/                 # Shared TS types
│  └─ utils/                 # Helpers (time, etc.)
├─ public/                   # Legacy demo UI (not used by the React app)
├─ scripts/
│  └─ seed-activity.js       # Local seeding helper to publish sample geohash events
├─ index.html                # Vite entry (React)
├─ vite.config.ts            # Vite + dev proxy for /api and /ws
├─ package.json              # Scripts + deps
└─ README.md                 # You are here

Requirements

  • Node.js 18+
  • npm 9+

Screenshots

Dual view with heatmap and sidebar

Map‑only view with clusters

Note: If you fork this repo and the images don’t render on GitHub, add your screenshots at:

  • assets/screenshots/dual-view.png
  • assets/screenshots/map-view.png

Quick start

# 1) Install deps
npm install

# 2) Run in development (Vite + Nodemon)
npm run dev

# 3) Seed some sample activity (optional, so you can see hotspots)
npm run seed   # sends ~20 random geohash events to the local server

Development scripts

npm run dev        # concurrently runs server (nodemon) + client (vite)
npm run build      # vite build -> dist/
npm start          # serve production build with Node server.js
npm run preview    # vite preview (static)
npm run seed       # publish random geohash events to local API
npx tsc --noEmit   # typecheck

Configuration

Environment variables (optional):

  • CACHE_TTL (seconds, default 300) – in‑memory cache retention
  • NOSTR_RELAYS – comma‑separated list of relay URLs. Defaults:
    wss://relay.damus.io,wss://nos.lol,wss://relay.primal.net,wss://offchain.pub,wss://nostr21.com,wss://nostr-pub.wellorder.net,wss://nostr.wine
    

Create a .env (optional):

CACHE_TTL=300
NOSTR_RELAYS=wss://relay.damus.io,wss://nos.lol

Vite dev server proxies API and WebSocket to the Node server (see vite.config.ts). In the browser, the client connects to ws(s)://<host>/ws.


How it works

  1. Server connects to configured Nostr relays and subscribes to kind 20000 events (geohash activity).
  2. It verifies events, caches recent activity per geohash, and broadcasts new activity to connected WebSocket clients.
  3. The React app renders:
    • Leaflet map with a vibrant heat layer and numeric badges for unique users per geohash.
    • A modern sidebar listing active geohash channels.
    • A chat timeline that updates live, with pause/auto‑scroll/search controls.
  4. Clicking a map badge or a channel focuses the map and filters the chat timeline to that geohash.

Unique‑user counts

  • Heat intensity and badge numbers reflect unique users (by npub fallback nickname) per geohash – not raw message count.

View switcher

  • Dual – Map and chat side‑by‑side. Sidebar is pinned and fully visible.
  • Chat – Only chat; sidebar collapses automatically.
  • Map – Only map; sidebar collapses automatically.
  • Clicking the same view again toggles back to your previous view.

REST API

The server exposes a small set of endpoints (see server.js):

  • GET /api/activity – recent geohash activities (flattened)
  • GET /api/heatmap-data – aggregate heatmap data by geohash
  • GET /api/relays – relay connection status
  • GET /api/coolr-test/:relay/:channel – helper for coolr.chat URLs
  • POST /api/keys/generate – generate Nostr keys (npub/nsec)
  • POST /api/keys/decode – decode npub/nsec
  • POST /api/lightning/validate – validate a Lightning address (lnurl)
  • POST /api/events/publish – publish a kind 20000 geohash event via local server
  • POST /api/zaps/create-request – create a NIP‑57 zap request payload
  • GET /api/users/:npub – aggregate recent activity for a user

Seeding data locally

The seed script publishes random events to your local server so you can see the map light up right away.

npm run seed
# or:
npm run seed -- 50   # send 50 events

The seeder generates a random 32‑byte hex private key locally (no external reliance) and posts to /api/events/publish.


Production

npm run build   # produces dist/
npm start       # node server.js (serves dist/ + ws + API)

Deploy anywhere you can run Node (Railway, Fly.io, Render, Heroku, a VM). Ensure websockets are enabled. Set NOSTR_RELAYS and PORT as needed.


Customizing & forking

  • Change the brand/title in src/App.tsx and theme in src/App.css (CSS variables at the top).
  • Sidebar behavior: the sidebar is forced open in Dual view (see App.tsx), and collapses automatically in Chat/Map.
  • Map tiles: switch the basemap by editing the tile layer in HeatmapView.tsx.
  • Heat gradient and badge thresholds: tunable in HeatmapView.tsx.
  • Relay set: define NOSTR_RELAYS in .env or your hosting provider.

When you publish your fork

  • Update this README’s title and links.
  • Consider adding a screenshot and a short demo clip.

Troubleshooting

  • “Tiles not visible” – We force normal blend mode for Leaflet tiles and use CartoDB dark tiles. If you swap tiles, ensure no CSS blend rules conflict.
  • WebSocket not connecting in dev – Ensure vite.config.ts proxy has /ws with ws: true, and the server is listening on 3001.
  • No data – Run npm run seed or confirm your relay list is reachable from your server.

License

MIT – see LICENSE (add one if missing).


Acknowledgements

  • Leaflet, leaflet.heat, Carto basemaps
  • Nostr protocol and the public relay ecosystem

About

This is a heatmap to show the geomesh locations and activity for Bitchat.

Topics

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Contributors 2

  •  
  •