Inspiration

Standing on the rim of Bryce Canyon this spring, I overheard two hikers debating which U.S. national park “deserved” the #1 spot on their bucket lists. Their phones were out, Google searches flying, but every “Top 10” article ranked parks differently, and most gave zero context on why you’d love one park more than another. It hit me: why keep rankings static when millions of park-lovers could decide in real time?

At the same time, the Bolt Hackathon challenge emphasized building something viral, community-powered, and fun. A head-to-head voting mechanic—think March-Madness-meets-nature—felt like the perfect way to:

  • Democratize discovery – let everyday explorers, not travel editors, surface hidden-gem parks.
  • Spark wanderlust – beautiful park photos + quick one-click votes make you want to keep exploring.
  • Give back to the parks – highlighting lesser-known parks can help spread visitor traffic and stewardship love.

Combining my obsession with national parks, a love for data-driven rankings (I’m a chess ELO nerd), and Bolt.new’s rapid-build power, ParkRanker was born.

What it does

ParkRanker turns the “best national park” debate into an addictive, community-driven game:

  1. Instant Matchups
    Every visit loads two random U.S. national parks side by side—complete with high-resolution photos, location, and a one-line teaser pulled from Wikipedia.

  2. One-Click Voting
    You click the park you’d rather visit. No sign-up, no friction.

  3. Real-Time ELO Scoring
    Behind the scenes, the vote feeds an ELO algorithm (borrowed from chess). Scores for both parks adjust instantly and the leaderboard reshuffles on the spot.

  4. Live Leaderboard & Trending Parks
    A dynamic table showcases the current top parks, movers of the day, and hidden gems climbing the ranks.

  5. Recent Votes Feed
    A rolling ticker shows the latest head-to-head results, creating a sense of live community activity.

  6. Zero-Friction Discovery Loop
    After each vote, the app immediately serves up a fresh matchup—encouraging endless exploration and shareability.

  7. Built for the Web, Anywhere
    Deployed on Netlify at https://growthgrail.net, with a custom Entri/IONOS domain integration. Fully responsive, so park lovers can rank from trailheads or couches alike.

  8. Data That Stays Fresh
    An Apify crawler pulls official park data and images from Wikipedia, pipes it into Supabase, and Bolt.new handles the frontend logic—ready for periodic re-crawls as parks evolve.

Result: ParkRanker gives outdoor enthusiasts a fun way to discover and champion their favorite national parks, while creating a living, community-validated ranking the internet has never seen before.

How we built it

1. Stack at a glance

Layer Tech Role
Frontend & Logic Bolt.new UI, routing, state, ELO algorithm, API calls
Database Supabase Tables (parks, votes, settings), real-time updates
Data Ingestion Apify Scrapes the National Parks Wikipedia table with image URLs
AI Summaries ChatGPT-4 Condenses park descriptions into 1-line summaries
Hosting Netlify CI/CD + global CDN deployment
Custom Domain Entri / IONOS Domain purchase & DNS (https://growthgrail.net)

2. Data pipeline

  1. Apify actor pulls raw fields: name, location, description, image_url.
  2. A Bolt serverless step calls ChatGPT-4 to convert each description into a crisp, <20-word summary.
  3. Enriched JSON is upserted into Supabase’s parks table with a default elo_score of 1500.
  4. Supabase broadcasts changes; Bolt components subscribe for live updates.

3. Core Bolt.new logic

  • MatchupPage – shows two random park cards → one-click vote → triggers ELO update.
  • Leaderboard – listens to Supabase’s parks channel and re-orders instantly.
  • RecentFeed – streams the latest 25 votes in real time.
  • ELO function (Bolt serverless): ```javascript const k = 32; const expect = (a, b) => 1 / (1 + 10 ** ((b - a) / 400)); export const updateElo = (w, l) => ({ newWinner: w + k * (1 - expect(w, l)), newLoser: l + k * (0 - expect(l, w)) }); Feature flag – auth_required = false in settings; ready to flip for future login.

Challenges we ran into

  1. Bolt’s web-crawling black hole
    I first asked Bolt to scrape the National Parks table directly from Wikipedia. It kept insisting it could “fix a few selectors and try again”… then quietly burned through 2.5 M+ tokens without ever returning clean data. Lesson learned: cut losses early. I pivoted to Apify, but that meant an extra day of reading docs and building a custom crawler from scratch.

  2. Moving data from Apify → Supabase
    Apify spit out beautiful JSON, but wiring an automated pipe into Supabase tables was trickier than expected. I fought through CORS issues and malformed rows before landing on a Bolt serverless function that pulls the Apify dataset and upserts it.

  3. Custom-domain DNS gymnastics
    Buying the domain via Entri + IONOS was one click. Getting it to actually resolve to my Netlify site was not. DNS propagation lag, mis-autoprovisioned CNAMEs, and a rogue AAAA record meant the site stayed on the Netlify subdomain for hours. I finally dove into both IONOS and Netlify dashboards to wire the records manually—glad I didn’t save that step for the last minute.

  4. Missing https: on Wikipedia thumbnails
    Wikipedia image URLs come back protocol-relative (//upload.wikimedia.org/...). Without the secure prefix, browsers block them. I added a tiny utility in Bolt to prepend https: before saving to Supabase.

  5. Supabase Row-Level Security (RLS) tweaks
    With authentication off for the MVP, I still needed public read access for the leaderboard but write access for server functions only. Crafting those RLS policies (and verifying them in real time) took multiple iterations.

  6. Display not optimized for mobile
    After deployment I discovered the desktop-first layout didn’t translate to phones—cards overlapped, tap targets were too small, and scrolling felt clunky. A mobile-first redesign is now top priority.

Accomplishments that we're proud of

  • Solo builder → live product
    Took the project from a sketch on paper to a fully functioning web app—design, data, code, and deployment—all on my own within the hackathon window.

  • End-to-end stack mastery
    Wired together Bolt.new, Supabase, Apify, ChatGPT-4, Netlify, and Entri/IONOS—six different platforms—into one seamless workflow.

  • Real-time ELO leaderboard
    Implemented chess-style scoring so every click instantly reshuffles park rankings—no page refresh needed.

  • AI-generated micro-content
    Used ChatGPT-4 to condense long Wikipedia park descriptions into punchy one-liners that fit perfectly on mobile cards.

  • Custom domain & SSL in 24 hrs
    Deployed the app to production on https://growthgrail.net, complete with HTTPS and a “Built on Bolt” badge.

  • Performance on a shoestring
    Despite rich images and live data, the site loads in under two seconds on 4G thanks to Netlify’s CDN and lazy-loaded thumbnails.

  • Resilient pivoting
    When Bolt’s crawler stalled, I learned Apify overnight, rebuilt the pipeline, and still shipped on time.

ParkRanker is now live, shareable, and ready for thousands of park lovers to play—proof that one determined builder can create viral-ready experiences with Bolt.

What we learned

  • Know when to pivot.
    Watching Bolt burn millions of tokens trying to crawl Wikipedia taught me to trust my gut and switch tools sooner rather than later. Apify rescued the data pipeline.

  • No-code ≠ no-thinking.
    Bolt.new handles a ton, but stitching six services together still requires architectural decisions—API contracts, rate limits, CORS, and security policies don’t magically disappear.

  • AI excels at micro-copy.
    ChatGPT-4 can crank out crisp, engaging one-liners far faster than I can write them. The trick is nailing the prompt and setting a tight word limit.

  • DNS is never “one click.”
    Buying a domain is easy; waiting for propagation and hunting stray records at 2 a.m. is not. Always budget time for domain + SSL hiccups.

  • ELO isn’t just for chess.
    The rating system translates beautifully to crowdsourced preferences—every vote feels meaningful, and the math keeps the leaderboard dynamic and fair.

  • Supabase RLS is powerful (and picky).
    Public read, server-only write policies took a few tries to get right, but once dialed in they make the app both secure and frictionless.

  • Performance matters—even in hackathons.
    Lazy-loading images and trimming JSON reduced load times to <2 s, which keeps users voting longer.

Most of all, I learned that with the right stack—and a willingness to pivot—you can turn a spark of an idea into a polished, share-worthy product in a single week.

What’s next for ParkRanker

  • Mobile-first redesign
    The desktop layout looks great, but the cards feel cramped on smaller screens. I’ll refactor the grid, add swipe-friendly gestures, and tighten image compression so the experience sings on phones.

  • Flip the auth feature flag
    Enable Supabase Auth so users can create profiles, track their voting history, and earn badges for streaks.

  • Social sharing hooks
    One-tap “Share this matchup” buttons (OG-image + park thumbnails) to boost viral reach on X, Reddit, and Instagram Stories.

  • Park deep-dive pages
    Click any park on the leaderboard to see best seasons, must-do hikes, and recent images pulled from NPS APIs.

  • Periodic data refresh
    Schedule the Apify crawler to rerun monthly, auto-updating new visitation stats or image changes in Supabase.

  • Themed tournaments
    Run limited-time brackets like “Best Winter Parks” or “Top Dark-Sky Spots,” resetting ELO within each event.

  • PWA & offline mode
    Cache images and data so users can keep voting (and dreaming) even when they’re off the grid in the backcountry.

  • Partnerships with park nonprofits
    Surface donation links or volunteer sign-ups for each park—turn discoverers into stewards.

These upgrades will polish the mobile experience, deepen engagement, and turn ParkRanker into the go-to community for national-park inspiration.

Built With

  • apify
  • bolt.new
  • chatgpt4
  • entri
  • ionos
  • netlify
  • supabase
Share this project:

Updates