    :Abmaj [:Ab :Bb :C :Db :Eb :F :G :Ab]})
 
 ;; -- components --
+
+(defn- create-event-handler
+  "Accepts a state atom, a key and a function, and
+  returns a callback function that sets the given key
+  value in the state map after transforming the event
+  target value with the function."
+  [state k f]
+  (fn [event]
+    (let [value (.. event -target -value)]
+      (swap! state assoc k (f value)))))
+
 (defn key-name-select-option
   "A dropdown box option for a particular musical key"
   [state idx k]

   [:h1 "Concert Pitch"]
   [:select {:style {:font-size "1.2em"}
             :value (:current-key @state)
             :onChange #(swap! state assoc :current-key (keyword (.. % -target -value)))}
    (doall
     (map-indexed (partial key-name-select-option state)
                  (sort (keys Bb-transpositions))))]])

(defn note-cell
  [state idx note]
  ^{:key (str "note-" idx)}
  [:th {:onClick #(sound/play (sound/octave note (:octave @state)))
        :style {:text-align "center"
                :font-weight "bold"
                :padding "4px"
