
How I built a fast, customizable map widget with Google Sheets integration to help a nonprofit community share accessible resources — no API keys or vendor lock-in required.

How I (tried to) recreated the iOS Messages invisible ink effect using React, Canvas, and a particle system
Introducing my new blog where I'll share insights on software engineering, web development, and building scalable systems.

How I built a fast, customizable map widget with Google Sheets integration to help a nonprofit community share accessible resources — no API keys or vendor lock-in required.

How I (tried to) recreated the iOS Messages invisible ink effect using React, Canvas, and a particle system
Introducing my new blog where I'll share insights on software engineering, web development, and building scalable systems.
OpenMapEmbed started the way many useful tools do: someone needed a real solution, and nothing off-the-shelf quite fit.
Earlier this year, I began working with AbleBodied.org, a nonprofit community for wheelchair users and adaptive athletes. Founded by Wojtek Poppe, AbleBodied's mission is to help people with spinal-cord injuries and disabilities reconnect with independence, movement, and outdoor recreation. They run educational content, equipment guides, local ambassadors, product testing, and community events.
I volunteered technical help — website improvements, accessibility cleanup, and custom software. One of the earliest feature requests was deceptively simple:
"Can we put an interactive, embeddable map on the site to show handcycle rental shops, ambassadors, accessible trails, and resources?"
I assumed we'd just embed Google Maps.
Turns out, not so simple.
Google Maps is polished and familiar… but it wasn't what AbleBodied actually needed. Wojtek wanted:
Google Maps can do some of this with enough duct tape, but not all of it gracefully. So instead of forcing the wrong tool to behave, I wrote a new one.
OpenMapEmbed is a fast, embeddable map + table widget built with:
It loads location data from CSV, Google Sheets, JSON, or any custom data adapter. Everything is responsive, mobile-friendly, and easy to theme.
GitHub Repo: github.com/foobarnes/open-map-embed
Live Demo: foobarnes.github.io/open-map-embed


Instead of hard-coding markers, the widget reads a data source and builds:
Add a row to your dataset → the map updates automatically.
Add a new column (like wheelchair_accessible, rental_price, photos, etc.) → the field appears in the popups and table. No code changes.
(Using the official embed script)
<div id="map-widget"></div><script src="https://foobarnes.github.io/openmapembed/dist/openmapembed.umd.js"></script>OpenMapEmbed.init({
container: '#map-widget',
dataSource: {
type: 'google-sheets-public',
sheetId: 'YOUR_SHEET_ID'
}
});You can also set:
theme: 'light' | 'dark'defaultView: 'map' | 'table'center: [lat, lng]zoom: numberFor full options, see the README.
The beauty of OpenMapEmbed is that your spreadsheet can be as simple or as detailed as you need.
Every location needs these five fields:
| Column | What It Does | Example |
|---|---|---|
| id | Unique identifier for each location | loc-001, ambassador-sf-1 |
| name | Display name | Joe's Bike Shop, Golden Gate Trail |
| latitude | GPS coordinate | 37.7749 |
| longitude | GPS coordinate | -122.4194 |
| category | Type of location | rental, ambassador, trail, shop |
Want richer popups and table data? Add these:
Address Fields
street (or address)citystatezip / zipcode / postal_codecountryContact Info
phoneemailwebsiteAdditional Details
description – longer texthours – freeform textimages – comma-separated image URLsurl – "more info" linkYou can add any column and OpenMapEmbed will:
rental_price → "Rental Price")For AbleBodied.org, some custom fields included:
wheelchair_accessible (Yes/No)rental_price ($50/day)bike_types_available (Handcycles, Recumbents)adaptive_equipment (Yes/No)staff_notesNo code changes. Just add the column and reload.
Categories are whatever you want — the widget will:
Works for:
["rental", "trail", "ambassador"]["pizza", "tacos", "burgers"]["haunted", "historic", "kid-friendly"]Minimal row:
id,name,latitude,longitude,category
loc-001,Joe's Bike Shop,37.7749,-122.4194,rentalFull row with custom fields:
id,name,latitude,longitude,category,description,street,city,phone,wheelchair_accessible,rental_price,bike_types_available
amb-sf-001,Joe's Bike Shop,37.7749,-122.4194,rental,Full adaptive bike rental,123 Market St,San Francisco,(555)123-4567,Yes,$50/day,"Handcycles, Recumbents"Add an images column with comma-separated URLs:
https://example.com/bike1.jpg, https://example.com/bike2.jpgThe popup becomes a scrollable gallery. Clicking opens full-size images.
.../d/YOUR_SHEET_ID/...)Once published, OpenMapEmbed will fetch and cache it. Updates typically show within 5 minutes.
CSV Template: location-data-template.csv
Setup Guide: Google Sheets Template Guide
If you want your own branding or tile server:
No backend. No API keys. No billing. Completely open.
Leaflet is amazing, but one bug tried to ruin my week: popups opening at the edge of the screen on mobile, especially inside iframes.
Default autopan logic jumped zoom levels or hid popups offscreen.
The fix required:
Now it feels smooth and effortless. But it cost real brain sweat.
For AbleBodied.org, this is more than a map. It's a live resource network:
The community controls the data. The map reflects it in real time.
But OpenMapEmbed works for:
If you want help integrating it, building a custom adapter, or hosting a branded fork, reach out anytime. The whole point of this project is to help communities share real-world resources, not fight with code.
OpenMapEmbed started the way many useful tools do: someone needed a real solution, and nothing off-the-shelf quite fit.
Earlier this year, I began working with AbleBodied.org, a nonprofit community for wheelchair users and adaptive athletes. Founded by Wojtek Poppe, AbleBodied's mission is to help people with spinal-cord injuries and disabilities reconnect with independence, movement, and outdoor recreation. They run educational content, equipment guides, local ambassadors, product testing, and community events.
I volunteered technical help — website improvements, accessibility cleanup, and custom software. One of the earliest feature requests was deceptively simple:
"Can we put an interactive, embeddable map on the site to show handcycle rental shops, ambassadors, accessible trails, and resources?"
I assumed we'd just embed Google Maps.
Turns out, not so simple.
Google Maps is polished and familiar… but it wasn't what AbleBodied actually needed. Wojtek wanted:
Google Maps can do some of this with enough duct tape, but not all of it gracefully. So instead of forcing the wrong tool to behave, I wrote a new one.
OpenMapEmbed is a fast, embeddable map + table widget built with:
It loads location data from CSV, Google Sheets, JSON, or any custom data adapter. Everything is responsive, mobile-friendly, and easy to theme.
GitHub Repo: github.com/foobarnes/open-map-embed
Live Demo: foobarnes.github.io/open-map-embed


Instead of hard-coding markers, the widget reads a data source and builds:
Add a row to your dataset → the map updates automatically.
Add a new column (like wheelchair_accessible, rental_price, photos, etc.) → the field appears in the popups and table. No code changes.
(Using the official embed script)
<div id="map-widget"></div><script src="https://foobarnes.github.io/openmapembed/dist/openmapembed.umd.js"></script>OpenMapEmbed.init({
container: '#map-widget',
dataSource: {
type: 'google-sheets-public',
sheetId: 'YOUR_SHEET_ID'
}
});You can also set:
theme: 'light' | 'dark'defaultView: 'map' | 'table'center: [lat, lng]zoom: numberFor full options, see the README.
The beauty of OpenMapEmbed is that your spreadsheet can be as simple or as detailed as you need.
Every location needs these five fields:
| Column | What It Does | Example |
|---|---|---|
| id | Unique identifier for each location | loc-001, ambassador-sf-1 |
| name | Display name | Joe's Bike Shop, Golden Gate Trail |
| latitude | GPS coordinate | 37.7749 |
| longitude | GPS coordinate | -122.4194 |
| category | Type of location | rental, ambassador, trail, shop |
Want richer popups and table data? Add these:
Address Fields
street (or address)citystatezip / zipcode / postal_codecountryContact Info
phoneemailwebsiteAdditional Details
description – longer texthours – freeform textimages – comma-separated image URLsurl – "more info" linkYou can add any column and OpenMapEmbed will:
rental_price → "Rental Price")For AbleBodied.org, some custom fields included:
wheelchair_accessible (Yes/No)rental_price ($50/day)bike_types_available (Handcycles, Recumbents)adaptive_equipment (Yes/No)staff_notesNo code changes. Just add the column and reload.
Categories are whatever you want — the widget will:
Works for:
["rental", "trail", "ambassador"]["pizza", "tacos", "burgers"]["haunted", "historic", "kid-friendly"]Minimal row:
id,name,latitude,longitude,category
loc-001,Joe's Bike Shop,37.7749,-122.4194,rentalFull row with custom fields:
id,name,latitude,longitude,category,description,street,city,phone,wheelchair_accessible,rental_price,bike_types_available
amb-sf-001,Joe's Bike Shop,37.7749,-122.4194,rental,Full adaptive bike rental,123 Market St,San Francisco,(555)123-4567,Yes,$50/day,"Handcycles, Recumbents"Add an images column with comma-separated URLs:
https://example.com/bike1.jpg, https://example.com/bike2.jpgThe popup becomes a scrollable gallery. Clicking opens full-size images.
.../d/YOUR_SHEET_ID/...)Once published, OpenMapEmbed will fetch and cache it. Updates typically show within 5 minutes.
CSV Template: location-data-template.csv
Setup Guide: Google Sheets Template Guide
If you want your own branding or tile server:
No backend. No API keys. No billing. Completely open.
Leaflet is amazing, but one bug tried to ruin my week: popups opening at the edge of the screen on mobile, especially inside iframes.
Default autopan logic jumped zoom levels or hid popups offscreen.
The fix required:
Now it feels smooth and effortless. But it cost real brain sweat.
For AbleBodied.org, this is more than a map. It's a live resource network:
The community controls the data. The map reflects it in real time.
But OpenMapEmbed works for:
If you want help integrating it, building a custom adapter, or hosting a branded fork, reach out anytime. The whole point of this project is to help communities share real-world resources, not fight with code.