diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json
new file mode 100644
index 0000000..418368f
--- /dev/null
+++ b/.devcontainer/devcontainer.json
@@ -0,0 +1,5 @@
+{
+  "tasks": {
+    "launch": "npx http-server"
+  }
+}
\ No newline at end of file
diff --git a/map.js b/map.js
index 6a92d67..3c0060d 100644
--- a/map.js
+++ b/map.js
@@ -43,9 +43,9 @@ async function initialize() {
   console.log('Loading Events...');
   window.events.load()
     .then(events => {
+      const now = new Date();
       events.forEach(event => {
-        if (!event.title) return; // skip empty events just in case
-        if (!event.geometry) console.error('Event Geometry Missing', { event });
+        if (new Date(event.end_time) < now) return; // skip events that have ended
         event.marker = new google.maps.marker.AdvancedMarkerElement({
           map: window.map,
           position: event.geometry,
@@ -111,6 +111,8 @@ window.filter = function (filters = {}) {
   if (options.category) {
     categories = options.category.split(',')
   }
+
+  const now = new Date();
     
   // Filter events
   let count = window.events.get().filter(event => {
@@ -126,11 +128,17 @@ window.filter = function (filters = {}) {
       )
         event.visible = false;
     }
+    
     // check category
     if (categories.length && !event.categories.some(category => categories.includes(category))) {
       event.visible = false;
     }
 
+    // check if event has ended
+    if (new Date(event.end_time) < now) {
+      event.visible = false;
+    }
+    
     // if (event.visible) {
     //   event.marker.content.classList.add('drop')
     // } else {
@@ -258,7 +266,7 @@ class Events {
     
     this.cache = window.localStorage.getItem('events');
 
-    if (this.cache)
+    if (this.cache && typeof this.cache === 'string')
       this.cache = JSON.parse(this.cache);
     
     return this.cache;
@@ -271,14 +279,23 @@ class Events {
    * @returns {object[]} events
    */
   set(events) {
+    const now = new Date()
+    // if (!event.title) return; // skip empty events just in case
+    // if (!event.geometry) console.error('Event Geometry Missing', { event });
+    // if (new Date(event.end_time) < new Date()) return; // skip events that have ended
+    const data = events.filter(event => 
+      event.title && 
+      event.geometry && 
+      (!event.end_time || new Date(event.end_time) > now)
+    );
     try {
       // Known to throw QuotaExceededException on Safari
-      window.localStorage.setItem('events', JSON.stringify(events));
+      window.localStorage.setItem('events', JSON.stringify(data));
       window.localStorage.setItem('events_age', Date.now());
     } catch (e) {
       console.error(e)
     }
-    this.cache = events;
+    this.cache = data;
     return this.cache;
   }