Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

6 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

📍 ProofPin

Social proof popups with a live map of every visitor.

Tiny corner popups that show real activity — and drop a pin on the visitor's city. Zero dependencies. One script tag. Bring your own brand.

npm license size no deps PRs welcome

🧭 Setup wizard · Quick start · Config


ProofPin popup showing a live map of each visitor's city

Most social-proof widgets show a boring avatar and a line of text. ProofPin drops a real map of the visitor's city behind an Apple-style pin — so "Sarah in Austin, TX just signed up" actually looks like it happened somewhere real. It reads as trustworthy because it is: ProofPin only ever shows the events you feed it.

┌───────────────────────────────────┐
│  ▓▓▓▓▓ │  Sarah in Austin, TX      │
│  ▓📍▓▓ │  Just started a free trial │
│  ▓▓▓▓▓ │  9 min ago · ✓ Verified   │
└───────────────────────────────────┘
     ▲ live map tile of their city

Why ProofPin

  • 🗺️ The map is the hook — a live, keyless map tile centered on each visitor's city.
  • 🪶 Zero dependencies — ~6kb gzipped, no framework, works on any site (HTML, WordPress, Shopify, React…).
  • 🎨 Your brand in minutes — logo, colors, pin, position, and copy are all config.
  • 🔌 Bring your own data — a static array, your own endpoint, or the included one-click backend.
  • 🔒 Honest by design — it shows only the real events you give it. No fake-event generator here (see Honest proof).
  • 🔔 5 notification types — visitor events (with the map), live count, announcements, countdown timers, and reviews.

ProofPin vs the paid tools

ProofPin Provely Fomo typical $29/mo tool
Price Free, MIT ~$17–95/mo ~$19–99/mo ~$29/mo
Live map of the visitor's city
Zero dependencies, one script tag
Self-hosted / your data
Visitor events + live count
Announcements / timers / reviews
Fabricated "past data" mode ✅ never ✅ (a feature) ⚠️ ⚠️
Open source

Same core features as the tools charging monthly — plus a live map they don't have, and no subscription.

🧭 Setup wizard (easiest)

→ Open the setup wizard — answer a few questions, watch the live preview, and copy a snippet that's already filled in with your brand, colors, and data source. No code to write.

Quick start (manual)

Drop in one script tag and initialize. This CDN link works today — no npm install needed:

<script src="https://cdn.jsdelivr.net/npm/proofpin"></script>
<script>
  ProofPin.init({
    logo: '/logo.svg',
    brandName: 'Acme',
    defaultAction: 'Just started a free trial',
    events: [
      { name: 'Sarah', place: 'Austin, TX', lat: 30.27, lon: -97.74, time: Date.now() - 540000 },
      { name: 'Marcus', place: 'Miami, FL', lat: 25.76, lon: -80.19, time: Date.now() - 120000 },
    ],
  })
</script>

That's the whole thing. No build step, no account. Paste it just before </body>.

Using a bundler instead? Install from npm:

npm install proofpin

Prefer no inline JS? Add a data-proofpin attribute with a JSON config to the script tag and ProofPin auto-initializes.

Where the data comes from

ProofPin renders whatever you give it. Pick one:

1. A static array — great for testimonials, demos, or when your backend already pushes events somewhere:

ProofPin.init({ events: [ /* {name, place, lat, lon, time, text} */ ] })

2. Your own endpoint — ProofPin GETs it and expects { events: [...], viewsToday: 0 }:

ProofPin.init({ endpoint: '/api/proof' })

3. The included backend (one click) — a Netlify Function + Netlify Blobs that captures real events, derives the city + map coordinates from the visitor's IP (server-side, never asked for), and counts a rolling 24h "viewing" number:

Deploy to Netlify

Then, from your app, record a real event whenever something real happens:

// after a genuine signup / purchase / booking:
fetch('/.netlify/functions/proofpin', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({ type: 'event', name: 'Sarah', text: 'Just started a free trial' }),
})

…and point the widget at it: ProofPin.init({ endpoint: '/.netlify/functions/proofpin' }).

Notification types

Besides visitor events, pass extra cards via cards: [...]. They're mixed into the rotation.

ProofPin.init({
  events: [ /* visitor events with the map */ ],
  cards: [
    { kind: 'announcement', title: 'New: v2 is live', text: 'Ship faster today',
      cta: 'Learn more', url: 'https://yoursite.com/v2' },
    { kind: 'timer', title: 'Launch ends in', until: Date.now() + 2*60*60*1000 },
    { kind: 'review', rating: 5, quote: 'Best tool we added all year.', name: 'Mia', place: 'NYC' },
  ],
})
  • announcementtitle, text, optional cta + url
  • timertitle, until (epoch ms); counts down live
  • reviewquote, rating (1–5), optional name, place
  • event — the visitor card (name, place, lat, lon, text, url)
  • count — the live "X people viewing" card (from viewsToday)

Any card with a url (or a global onClick) becomes clickable.

Push events live

Fire a notification the moment something real happens — no reload:

ProofPin.init({ /* ... */ })
// later, e.g. inside your signup success handler:
ProofPin.push({ name: 'Sarah', place: 'Austin, TX', lat: 30.27, lon: -97.74, text: 'Just signed up' })
// or any card type:
ProofPin.push({ kind: 'review', rating: 5, quote: 'Love it!', name: 'Dev' })

Configuration

Every option, with defaults:

Option Default What it does
endpoint null URL to GET events from ({ events, viewsToday }).
events null Static array of events instead of an endpoint.
viewsToday 0 Count to use with a static events array.
recordViews true POST {type:'view'} to the endpoint on load.
position 'bottom-left' bottom-left, bottom-right, top-left, or top-right.
cards null Extra cards (announcement / timer / review) mixed into the rotation.
onClick null fn(item) fired when a card is clicked.
maxDisplays 0 Stop after N cards per session (0 = unlimited).
showOnMobile true false hides the widget on screens under 640px.
pages null Only show on matching paths, e.g. ['/pricing','/blog/*'] (null = everywhere).
gradient ['#7D4AFE','#4399EC','#07EBDB'] Brand accent gradient.
accent '#4399EC' "Verified by" / accent color.
pinColor '#FF3B30' The map drop pin.
dark true Dark glass card (false = light card).
logo null Small logo URL for the "Verified by" badge.
brandName 'us' Shown as "Verified by {brandName}" when no logo.
verifiedText 'Verified by' Label before the logo/brand.
showVerified true Show the verified badge line.
showMap true Show the map panel (falls back to gradient + pin).
mapZoom 12 Map zoom (higher = tighter on streets).
defaultAction 'Just signed up' Action line; per-event text overrides it.
countText '{n} people viewing recently' Headline for the count card ({n} = viewsToday).
countSubtext 'in the last 24 hours' Sub-line for the count card.
viewFloor 8 Hide the count card below this number.
maxAgeHours 48 Ignore events older than this.
firstDelay 5000 Delay (ms) before the first popup.
visibleMs 6500 How long each popup stays (ms).
gapMs 2800 Gap between popups (ms).
dismissible true Show the ✕ dismiss button.
respectReducedMotion true Honor prefers-reduced-motion.
zIndex 2147483000 Stacking order.

Event shape

{
  name: 'Sarah',        // first name (kept short)
  place: 'Austin, TX',  // shown next to the name
  lat: 30.27,           // optional — enables the map tile
  lon: -97.74,          // optional
  time: 1700000000000,  // epoch ms (or `t`)
  text: 'Just signed up' // optional per-event action line
}

No lat/lon? The card gracefully falls back to your brand gradient with the pin.

API

const pin = ProofPin.init(config) // start
pin.push(item)                    // add a card/event live (see "Push events live")
pin.dismiss()                     // stop and hide
pin.destroy()                     // remove entirely
ProofPin.push(item)               // push to the most recent instance
ProofPin.version                  // e.g. "0.2.0"

Honest proof

ProofPin ships without a fake-event generator, on purpose. Invented social proof ("Someone just bought!" when nobody did) is deceptive and, in many places, illegal (e.g. the US FTC's rules on fake endorsements). ProofPin only renders the events you feed it — so feed it real ones, fired from real signups, purchases, or bookings. Honest proof converts and keeps you out of trouble. This tool just makes it look great.

The map

Map tiles come from CARTO Voyager basemaps (no API key needed for light use). Attribution for OpenStreetMap and CARTO is required if you display the map — add a small line in your footer:

Maps © OpenStreetMap contributors, © CARTO

Higher traffic? Swap the one TILE constant in src/proofpin.js for a keyed provider (Mapbox, MapTiler, etc.).

Browser support

All modern browsers (Chrome, Safari, Firefox, Edge). Uses fetch and backdrop-filter; degrades gracefully where backdrop-filter is unsupported.

Development

npm install       # dev deps (esbuild, @netlify/blobs)
npm run build     # -> dist/proofpin.min.js (+ copies into demo/)

Open docs/index.html to run the setup wizard locally.

Contributing

Issues and PRs welcome. Keep it dependency-free and small.

License

MIT © Jay Awal (@mr.jayawal)

About

Social proof popups with a live map of every visitor. Zero dependencies, one script tag.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages