{"id":15540342,"url":"https://github.com/rm-hull/big-bang","last_synced_at":"2025-04-23T16:43:56.256Z","repository":{"id":13158034,"uuid":"15840812","full_name":"rm-hull/big-bang","owner":"rm-hull","description":"ClojureScript event loop abstraction, loosely based on Racket's big-bang and implemented on top of core.async","archived":false,"fork":false,"pushed_at":"2014-11-23T22:25:06.000Z","size":1041,"stargazers_count":65,"open_issues_count":5,"forks_count":8,"subscribers_count":6,"default_branch":"master","last_synced_at":"2024-10-03T12:18:01.791Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"Clojure","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/rm-hull.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.md","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2014-01-12T11:11:58.000Z","updated_at":"2024-05-11T06:32:32.000Z","dependencies_parsed_at":"2022-08-30T14:10:09.076Z","dependency_job_id":null,"html_url":"https://github.com/rm-hull/big-bang","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rm-hull%2Fbig-bang","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rm-hull%2Fbig-bang/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rm-hull%2Fbig-bang/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rm-hull%2Fbig-bang/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/rm-hull","download_url":"https://codeload.github.com/rm-hull/big-bang/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":250472245,"owners_count":21436106,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-10-02T12:13:34.200Z","updated_at":"2025-04-23T16:43:56.240Z","avatar_url":"https://github.com/rm-hull.png","language":"Clojure","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Big-Bang [![Build Status](https://secure.travis-ci.org/rm-hull/big-bang.png)](http://travis-ci.org/rm-hull/big-bang)\n\nBig-Bang is a ClojureScript game-loop / event-loop abstraction, loosely based on\nRacket's [big-bang][a] and implemented on top of [core.async][b]. It is a pure\nClojureScript implementation with no external Javascript dependencies. Using\n_Big-Bang_ encourages you to implement what would be otherwise stateful code\nin a pure functional manner; of course, inevitably, at some point you have to\npunch outside and twiddle some IO or paint some pixels - this can be entirely\nencapsulated in the render handler however.\n\nSee [Pacman](http://rm-hull.github.io/big-bang/example.html) (work-in-progress), a\n[simple animation](http://programming-enchiladas.destructuring-bind.org/rm-hull/8623502), a\n[tumbling 3D torus](http://programming-enchiladas.destructuring-bind.org/rm-hull/7098992),\n[parametric equations](http://programming-enchiladas.destructuring-bind.org/rm-hull/8776719), and\n[Rock Paper Scissors](http://programming-enchiladas.destructuring-bind.org/rm-hull/8723389)\nfor demos, and for a code comparison between Big-Bang and [OM][c], see here:\n\n[Om mouse move][d] vs. [Big Bang mouse move][e] ツ\n\n[a]: http://docs.racket-lang.org/teachpack/2htdpuniverse.html#(form._world._((lib._2htdp/universe..rkt)._big-bang))\n[b]: http://github.com/clojure/core-async\n[c]: https://github.com/swannodette/om\n[d]: http://programming-enchiladas.destructuring-bind.org/rm-hull/8617445\n[e]: http://programming-enchiladas.destructuring-bind.org/rm-hull/8617788\n\n### Pre-requisites\n\nYou will need [Leiningen](https://github.com/technomancy/leiningen) 2.3.4 or\nabove installed.\n\n### Building \u0026 Testing\n\nTo build and install the library locally, run:\n\n    $ lein cljsbuild once\n    $ lein install\n\nTo test against [PhantomJS](http://phantomjs.org/), ensure that that\npackage is installed properly, and run:\n\n    $ lein cljsbuild test\n\nAlternatively, open ```resources/run-tests.html``` in a browser - this\nexecutes the tests and the test results are displayed on the page.\n\n### Including in your project\n\nThere is an 'alpha-quality' version hosted at [Clojars](https://clojars.org/rm-hull/big-bang).\nFor leiningen include a dependency:\n\n```clojure\n[rm-hull/big-bang \"0.0.1-SNAPSHOT\"]\n```\n\nFor maven-based projects, add the following to your `pom.xml`:\n\n```xml\n\u003cdependency\u003e\n  \u003cgroupId\u003erm-hull\u003c/groupId\u003e\n  \u003cartifactId\u003ebig-bang\u003c/artifactId\u003e\n  \u003cversion\u003e0.0.1-SNAPSHOT\u003c/version\u003e\n\u003c/dependency\u003e\n```\n## Basic Usage\n\nBig bang takes a number of keyword arguments in its call, most of which are optional;\nonly ```:initial-state``` and ```:to-draw``` are required.\n\nThe ```:initial-state``` value should be a persistent data-structure which typically\nmeans using a map or vector. It should be the basis of the starting state for the component.\n\nEvent handlers can be attached by adding a key starting with ```:on-...``` (for\nexample ```:on-keydown```, ```:on-click```) and a value of a function handler\nreference: the function should take two arguments: an event and a world-state,\nand return a (possibly modified) world state. The event naming follows typical\njavascript event types, and unless a DOM element is specified in ```:event-target```,\nthe events are bound to _document.body_. If you find yourself wanting to bind to\nmultiple events from different targets within a single big-bang world, this is a\nclear sign that you should be using multiple (smaller) big-bang components instead.\n\n**NOTE:** There are two reserved event handlers:\n\n* ```:on-tick``` which if present, invokes an interval timer every 17ms,\n  (i.e. an effective frame rate of approx 60 frames/sec) or whatever rate\n  is defined by ```:tick-rate```. If not defined, then no timer event\n  source will be installed,\n\n* ```:on-receive``` which is a handler that is invoked\n  when external messages are received (on the ```:receive-channel```).\n\nOn start-up, all the event sources are assembled and any initialization occurs.\nInside a go loop, the associated channels (/ports) are passed to a _core.async_\nalts! method. The resulting value ('the received event') is then dispatched to\nthe relevant handler function along with the world-state.\n\nIt is _part of the expected contract between big-bang and the event handlers_ that:\n\n* The handler function accepts an event and world-state as arguments, and that\n  a modified world-state is returned. The world state can be packaged to\n  include a message which will be emitted on the ```:send-channel``` - just\n  return ```(make-package modified-world-state message)``` instead.\n\n* To take advantage of structural sharing, rather than creating new states,\n  you are encouraged to use ```assoc```, ```assoc-in```, ```merge``` or\n  ```update-in``` to modify the existing world state. The threading macros\n  (```-\u003e``` and ```-\u003e\u003e```) are particularly useful constructs for making this\n  intent clear.\n\n* The handler function _should_ generally be idempotent and free from side effects:\n  the event and the incoming world-state are the only things that will effect the\n  returned world-state.\n\nThe new world-state is compared to the old world-state, and if\nthere is a difference, then the ```:to-draw``` renderer is invoked. The renderer\nhandler accepts a single argument: the world-state. It should completely render\nthe component to the DOM according to the supplied world-state, whether this is\ncanvas operations or via some DOM manipulation library such as\n[Dommy](https://github.com/Prismatic/dommy).\n\nBig-bang can be terminated by supplying a ```:stop-when?``` handler (accepting a\nsingle argument of the world-state, returning true will cease the event loop), or\nafter a fixed number of frames specified by the ```:max-frames``` value. By default,\nit will run indefinitely however.\n\n### A simple example\n\nThe following code sample gives a simple high level overview of how to program\na big-bang world. See http://programming-enchiladas.destructuring-bind.org/rm-hull/8623502\nfor a running demo of this code:\n\n```clojure\n(ns big-bang.example.cat-animation\n  (:require-macros [cljs.core.async.macros :refer [go]])\n  (:require [enchilada :refer [canvas ctx canvas-size proxy-request]]\n            [cljs.core.async :as async :refer [\u003c!]]\n            [dataview.loader :refer [fetch-image]]\n            [big-bang.core :refer [big-bang!]]\n            [jayq.core :refer [show attr]]))\n\n(defn increment-and-wrap [x]\n  (if (\u003c x 800)\n    (inc x)\n    0))\n\n(defn update-state [event world-state]                      ; [1]\n  (update-in world-state [:x] increment-and-wrap))\n\n(defn render-scene [ctx img {:keys [x y] :as world-state}]  ; [2]\n  (.clearRect ctx 0 0 800 220)\n  (.drawImage ctx img x y))\n\n(go\n  (let [cat \"https://gist.github.com/rm-hull/8859515c9dce89935ac2/raw/cat_08.jpg\"\n        img (\u003c! (fetch-image (proxy-request cat)))]         ; [3]\n\n    (attr canvas \"width\" 800)\n    (attr canvas \"height\" 220)\n    (show canvas)\n\n    (big-bang!                                              ; [4]\n      :initial-state {:x 0 :y 0}\n      :on-tick update-state\n      :to-draw (partial render-scene ctx img))))\n```\n\nAt step [3], an image is fetched asynchronously bound to a local context\ninside the ```go``` block - then big-bang is invoked at [4] with an initial world-state\nof ```[0 0]``` - the co-ordinates of the canvas onto which we will render\nthe image.\n\nThe big-bang internal ticker is initialized to tick every 17ms (~60FPS) by default,\nand call ```update-state``` defined at [1]. This function takes the event (unused)\nand a world-state, and returnd a new world-state comprising an updated X component\nalong with the unmodified Y value.\n\nOn world-state being changed, the to-draw ```render-scene``` function at [2] is scheduled\nto run inside a _requestAnimationFrame()_ callback. Internally the world-state is\nadvanced in a recursive call, ready to dispatch on the next incoming event: this is\nbut an implementation detail that needn't be dwelt on, however.\n\n### Event Handling and ```IChannelSource```\n\nThe ```big-bang.event-handler``` namespace provides a function that installs\nevent-listeners onto DOM elements, and rather than implementing a callback\narchitecture, the handler instead returns a reified ```IChannelSource``` object;\nthis exposes a channel onto which events are placed and a facility to de-install\nthe event listener.\n\n```clojure\n(def listener (add-event-listener (.-body js/document) :click))\n\n(go\n  (loop []\n    (when-let [e (\u003c! (data-channel listener))]\n      (.log js/console (str \"Received: \" e))\n      (recur)))))\n\n(go\n  (\u003c! (timeout 20000))\n  (shutdown! listener))\n```\n\nMultiple event listeners are used internally to drive state transitions\nin the ```big-bang!``` game loop on key presses, mouse events, etc.\n\n### Regular Ticking\n\nThe ```big-bang.timer``` namespace provides a mechanism that wraps the\njavascript ```setInterval``` callback, sending a predetermined payload\non a channel at regular intervals:\n\n```clojure\n(def ticker (interval-ticker 100))              ; [1]\n\n(go\n  (loop []\n    (when-let [x (\u003c! (data-channel ticker))]    ; [2]\n      (.log js/console (str \"Received: \" x ))\n      (recur))))\n\n(go\n  (\u003c! (timeout 2000)) ; pause for a short time  ; [3]\n  (shutdown! ticker))                           ; [4]\n```\n\nNotice the similarity to the prior event listener example above.\n\nAt [1], a ticker is created and started; note that, while there is no consumer\ntaking events from the timer channel, messages are dropped in order to prevent\na backlog. Similarly if the consumer loop cannot keep up with the rate that\nthe ticker is producing, then events will similarly be dropped.\n\nAt [2], inside a go block, a value is consumed from the ticker's timer channel.\nIf a ```nil``` value is returned from the channel, it can be assumed that the\nticker has elsewhere been stopped, and the loop can be terminated. Step [3] then\nincurs a 2 second delay before stopping the ticker at step [4].\n\nThe ticker is used internally to drive state transitions in the ```big-bang!```\ngame loop.\n\n## Differences from the Racket implementation\n\nThis library is written in the _spirit_ of Racket's big-bang - it is not intended\nas a like-for-like copy, nor I have not poured over the implementation details of\nbig-bang. Inevitably, therefore, there will be differences between the two, which\nI will attempt to document here:\n\n\n## TODO\n\nMigrated the TODO list to github issues: http://github.com/rm-hull/big-bang/issues\n\n## Known Bugs\n\n* ~~```lein cljsbuild test``` does not appear to be working properly, returns _\"Could not locate test command.\"_~~\n\n## References\n\n* http://docs.racket-lang.org/teachpack/2htdpuniverse.html#(form._world._((lib._2htdp/universe..rkt)._big-bang))\n* http://www.ccs.neu.edu/racket/pubs/icfp09-fffk.pdf\n* http://worrydream.com/Tangle/\n* https://github.com/oakes/play-clj\n\n## License\n\nThe MIT License (MIT)\n\nCopyright (c) 2014 Richard Hull\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of\nthis software and associated documentation files (the \"Software\"), to deal in\nthe Software without restriction, including without limitation the rights to\nuse, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of\nthe Software, and to permit persons to whom the Software is furnished to do so,\nsubject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS\nFOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR\nCOPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER\nIN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\nCONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n\n\n[![Bitdeli Badge](https://d2weczhvl823v0.cloudfront.net/rm-hull/big-bang/trend.png)](https://bitdeli.com/free \"Bitdeli Badge\")\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frm-hull%2Fbig-bang","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Frm-hull%2Fbig-bang","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frm-hull%2Fbig-bang/lists"}