DEV Community

DINESH S
DINESH S

Posted on

How I Built DisasterWatch: A Real-Time Earthquake Monitoring System




TL;DR

I built a production-ready disaster monitoring app in 30 days using vanilla JavaScript. Here's what I learned about performance, API optimization, and shipping real products.

πŸ”— Live Demo | Source Code


Table of Contents

  1. Why I Built This
  2. Tech Stack & Architecture
  3. Key Features
  4. Technical Challenges
  5. Performance Optimization
  6. Lessons Learned
  7. What's Next

Why I Built This {#why}

Three months into learning JavaScript, I hit tutorial hell. I could follow along, but couldn't build anything independently.

DisasterWatch was my "sink or swim" project. Here's why I chose it:

  1. Real data - Working with live APIs (USGS, Open-Meteo)
  2. Complex state - Managing filters, pagination, caching
  3. Performance challenges - Rendering 10,000+ data points
  4. Social impact - Helping communities stay informed

Tech Stack & Architecture {#tech-stack}

Zero dependencies. Just vanilla JavaScript, HTML5, and CSS3.

Why no frameworks?

  • Master the fundamentals first
  • Understand what frameworks abstract away
  • Smaller bundle size (45KB total)
  • Faster load times

File Structure

disaster-dashboard/
β”œβ”€β”€ index.html          # Semantic HTML5
β”œβ”€β”€ css/
β”‚   └── style.css       # CSS Custom Properties + Grid
β”œβ”€β”€ js/
β”‚   β”œβ”€β”€ config.js       # API endpoints & constants
β”‚   β”œβ”€β”€ utils.js        # 30+ helper functions
β”‚   β”œβ”€β”€ api.js          # Data fetching & caching
β”‚   └── app.js          # State management + UI
Enter fullscreen mode Exit fullscreen mode

Why this structure?

Separation of concerns. Each file has one responsibility:

  • config.js - Configuration (what)
  • utils.js - Utilities (how)
  • api.js - Data layer (fetch)
  • app.js - Application logic (orchestrate)

Key Features {#features}

1. Real-Time Data

// Fetch earthquakes with caching
async function fetchEarthquakes(timeframe = 'day') {
  const cacheKey = `earthquakes_${timeframe}`;

  // Check cache first
  const cached = cache.get(cacheKey);
  if (cached) return cached;

  // Fetch from API
  const data = await fetch(EARTHQUAKE_API_URL);
  const earthquakes = await data.json();

  // Cache for 5 minutes
  cache.set(cacheKey, earthquakes, 300000);

  return earthquakes;
}
Enter fullscreen mode Exit fullscreen mode

Why caching matters:

  • First load: 500ms (network request)
  • Cached load: 5ms (memory read)
  • 100x faster!

2. Smart Filtering

Users can filter by:

  • Magnitude (2.5+, 4.0+, 5.0+, etc.)
  • Time period (hour, day, week, month)
  • Location (search by place name)
  • Proximity (earthquakes near user)

3. Performance Optimization

The app handles 10,000+ earthquakes without lag.

How?

// Pagination instead of rendering everything
const itemsPerPage = 20;
const displayedItems = earthquakes.slice(0, itemsPerPage);

// Load more on button click
function loadMore() {
  currentPage++;
  const start = currentPage * itemsPerPage;
  const end = start + itemsPerPage;
  const moreItems = earthquakes.slice(start, end);

  render(moreItems);
}
Enter fullscreen mode Exit fullscreen mode

Result: 0.9s initial load vs 8.2s rendering everything.

4. Accessibility

  • βœ… WCAG 2.1 compliant
  • βœ… Keyboard navigation
  • βœ… Screen reader support
  • βœ… Proper ARIA labels
  • βœ… Focus management

Example:

<button 
  aria-label="Toggle dark mode"
  aria-pressed="false"
  id="theme-toggle">
  <i class="fas fa-moon" aria-hidden="true"></i>
</button>
Enter fullscreen mode Exit fullscreen mode

Technical Challenges & Solutions {#challenges}

Challenge 1: API Rate Limiting

Problem: Free tier = 60 requests/hour

Solution: Implement caching layer

const cache = new Map();

function set(key, value, ttl) {
  cache.set(key, {
    data: value,
    expires: Date.now() + ttl
  });
}

function get(key) {
  const item = cache.get(key);
  if (!item) return null;

  // Check if expired
  if (Date.now() > item.expires) {
    cache.delete(key);
    return null;
  }

  return item.data;
}
Enter fullscreen mode Exit fullscreen mode

Impact: Reduced API calls by 85%

Challenge 2: Search Performance

Problem: Search fired on every keystroke = 10 calls per word

Solution: Debouncing

function debounce(func, delay) {
  let timeout;
  return function(...args) {
    clearTimeout(timeout);
    timeout = setTimeout(() => func(...args), delay);
  };
}

// Usage
const debouncedSearch = debounce(handleSearch, 500);
searchInput.addEventListener('input', debouncedSearch);
Enter fullscreen mode Exit fullscreen mode

Impact: 90% fewer API calls

Challenge 3: Mobile Performance

Problem: Large datasets crashed mobile browsers

Solution: Progressive enhancement

  • Desktop: Show 50 items initially
  • Mobile: Show 20 items initially
  • Use matchMedia to detect device
const isMobile = window.matchMedia('(max-width: 768px)').matches;
const itemsToShow = isMobile ? 20 : 50;
Enter fullscreen mode Exit fullscreen mode

Performance Metrics {#performance}

Before Optimization:

  • First Contentful Paint: 3.2s
  • Time to Interactive: 8.1s
  • Lighthouse Score: 62

After Optimization:

  • First Contentful Paint: 0.8s ⚑
  • Time to Interactive: 1.4s ⚑
  • Lighthouse Score: 95 🎯

Key optimizations:

  1. API caching
  2. Debounced search
  3. Pagination
  4. DOM element caching
  5. Lazy loading images
  6. Minified CSS/JS

Lessons Learned {#lessons}

1. Start Small, Iterate Fast

Don't build everything at once.

My approach:

  • Week 1: Basic UI + API connection
  • Week 2: Filtering & search
  • Week 3: Performance optimization
  • Week 4: Polish & deploy

2. Test on Real Devices

Chrome DevTools mobile emulator β‰  Real iPhone

Issues I found only on real devices:

  • Touch target too small
  • Text too tiny
  • API too slow on 3G

3. Accessibility from Day 1

Don't bolt it on at the end. Build it in.

Easy wins:

  • Semantic HTML (<header>, <main>, <nav>)
  • Alt text on images
  • ARIA labels on buttons
  • Keyboard navigation

4. Read Documentation

MDN > Stack Overflow > Random blogs

When stuck:

  1. Read error message carefully
  2. Check MDN docs
  3. Console.log everything
  4. Then Google specific error

5. Ship Imperfect

Perfect is the enemy of done.

I wanted to add:

  • Interactive map (Leaflet.js)
  • Historical charts
  • Email alerts
  • PWA support

But I shipped with core features. Rest can come later.


What's Next {#next}

Short-term:

  • [ ] Add interactive map
  • [ ] Implement service workers (PWA)
  • [ ] Add Chart.js visualizations
  • [ ] Multi-language support

Long-term:

  • [ ] Rebuild with React (for learning)
  • [ ] Add Node.js backend
  • [ ] MongoDB for historical data
  • [ ] Real-time WebSocket updates

Final Thoughts

Three months ago, I couldn't build a working button.

Today, I shipped a production app that:

  • Handles 10,000+ data points
  • Loads in under 1 second
  • Works on all devices
  • Helps communities stay safe

You can too.

Start with something that scares you.

Break it into tiny pieces.

Ship something imperfect.

Iterate.

That's how you learn.


Let's Connect!

I'm looking for junior developer roles or internships!

Found this helpful? Share it with someone learning web development! πŸš€


Tags: #javascript #webdev #beginners #tutorial #webperformance #learntocode


Enter fullscreen mode Exit fullscreen mode

Top comments (0)