A little Godfried Toussaint inspired polyrhythmic drum toy online at http://jackrusher.com/drum-circle/
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(ns drum-machine.core | |
(:require [om.core :as om :include-macros true] | |
[sablono.core :as html :refer-macros [html]])) | |
(enable-console-print!) | |
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |
;; UI | |
(def tau 6.2831853071) | |
(defn build-drum [radius sides] | |
(mapv #(vector (* radius (.cos js/Math (* tau (/ % sides)))) | |
(* radius (.sin js/Math (* tau (/ % sides)))) | |
false | |
%) ; or: (int (* % (/ tempo sides))) | |
(range (inc sides)))) | |
(def drum-radii [145 95 45]) | |
(defonce app-state | |
(atom {:drums (mapv build-drum drum-radii [8 8 4])})) | |
(defn widget [app] | |
(om/component | |
(html | |
[:div {:id "drum-container"} | |
[:svg {:id "drum-circles" :viewBox "-155 -160 320 320" :preserveAspectRatio "xMaxYmax" | |
:height "100%" :width "100%"} | |
(for [[i vertices] (map-indexed vector (:drums app))] | |
[:g | |
[:polygon {:points (apply str (for [[x y] vertices] (str x "," y " "))) | |
:stroke "#ECE5CE" | |
:stroke-width "2" | |
:fill "#E08E79" | |
:fill-opacity "0.33"}] | |
(for [[x y active n] (butlast vertices)] | |
(let [handler (fn [e] | |
(.preventDefault e) | |
(om/update! app [:drums i n 2] (not active)))] | |
[:circle {:id (str i "-" n) | |
:cx (str x) | |
:cy (str y) | |
:r 8 | |
:fill (if active "#E08E79" "#ECE5CE") | |
:onTouchEnd handler | |
:onClick handler }]))])] | |
(for [i (range 3)] | |
[:input {:id (str "slider" i) | |
:type "range" :value (dec (count (nth (:drums app) i))) :min 3 :max 16 | |
:style {:width "100%"} | |
:on-change #(om/update! app [:drums i] | |
(build-drum (drum-radii i) | |
(-> % .-target .-value js/parseInt)))}])]))) | |
(om/root widget app-state {:target js/document.body}) | |
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |
;; Play loop | |
(defonce sounds | |
[(js/Howl. (js-obj "urls" (array "sounds/kick.wav"))) | |
(js/Howl. (js-obj "urls" (array "sounds/snare.wav"))) | |
(js/Howl. (js-obj "urls" (array "sounds/closed-hat.wav")))]) | |
(defonce interval-id (atom 0)) | |
(defonce current-beat (atom 0)) | |
(defn run-beat [] | |
(swap! current-beat inc) | |
(doseq [[i vertices] (map-indexed vector (:drums @app-state))] | |
(doseq [[x y active n] (butlast vertices)] | |
(if (= n (mod @current-beat (dec (count vertices)))) | |
(do | |
(.setAttribute (.getElementById js/document (str i "-" n)) "r" "10") | |
(when active (.play (sounds i)))) | |
(.setAttribute (.getElementById js/document (str i "-" n)) "r" "8") )))) | |
(defn stop-beat [] | |
(js/clearInterval @interval-id)) | |
(defn start-beat [] | |
(stop-beat) | |
(reset! interval-id (js/setInterval run-beat 250))) | |
(start-beat) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment