     scrollY = window.scrollY 
     innerHeight = window.innerHeight 
 
-    # if performance tuning, this could be improved by doing a binary search in els 
-    # (assuming sorted by offsetY) to find the visible elements more efficiently
-    for el in els 
-      slop = parseInt(el.getAttribute('data-receive-viewport-visibility-updates') or 1)
-      visible = is_el_visible el, scrollY, innerHeight, slop
+    el_sets = {}
+    for el in els
+      slop = parseFloat(el.getAttribute('data-receive-viewport-visibility-updates') or 1)
+      name = el.getAttribute 'data-visibility-name'
+      key = "#{name}-#{slop}"
 
-      state = fetch el.getAttribute 'data-component'
-      if !state.in_viewport? || state.in_viewport != visible 
-        state.in_viewport = visible 
-        save state
+      el_sets[key] ?= []
+      el_sets[key].push el
+
+
+    # Use previous visibility values to find one visible element, then figure out which elements 
+    # around it are also visible. We assume that the elements are sorted by global 
+    # offset Y here. This is usually true, but we can get in trouble. Set 
+    # data-visibility-name if there's a problem with the assumption. 
+    for __, els of el_sets
+      visibility = {}
+      # slop will be same for all elements in this set
+      slop = parseFloat(els[0].getAttribute('data-receive-viewport-visibility-updates') or 1)
+      len = els.length
+      start_here = Math.floor(len / 2)
+      found_visible = false
+      for el, idx in els 
+        state = fetch el.getAttribute 'data-component'
+        if state.in_viewport
+          visibility[idx] = is_el_visible els[idx], scrollY, innerHeight, slop
+          if visibility[idx]
+            start_here = idx
+            found_visible = true
+           
+
+      # look for visible elements around this element
+      for step in [1,-1]
+        inc = start_here
+
+        while inc >= 0 && inc < len
+          visibility[inc] ?= is_el_visible els[inc], scrollY, innerHeight, slop
+          if !visibility[inc] && found_visible
+            break
+          inc += step
+
+      # set visibility of elements
+      for el, idx in els 
+        visible = !!visibility[idx]
+        state = fetch el.getAttribute 'data-component'
+        # el.setAttribute 'data-in-viewport', visible
+        if !state.in_viewport? || state.in_viewport != visible 
+          state.in_viewport = visible 
+          save state
+
 
   update_scheduled = false
 

      break
    parent = parent.offsetParent

  el_bottom = el_top + h 

  topVisible =    -slop * h < el_top && el_top < h * 2 * slop
  bottomVisible = -slop * h < el_bottom && el_bottom < h * 2 * slop    
  visible = topVisible || bottomVisible

  visible


sense_viewport_visibility_changes = ->

update_scheduled = false 


window.is_el_visible = (el, scrollY, viewport_height, slop) ->
  slop ?= 1
  slop -= 1
  el_top = -scrollY
  vh = viewport_height

  parent = el 
  while (true)
