{"id":51630254,"url":"https://github.com/flow-lang/flow-framework","last_synced_at":"2026-07-13T06:33:04.025Z","repository":{"id":44079630,"uuid":"197737895","full_name":"flow-lang/flow-framework","owner":"flow-lang","description":"An Elm-inspired framework for interactive Web Audio applications.","archived":false,"fork":false,"pushed_at":"2023-01-07T09:01:56.000Z","size":1002,"stargazers_count":9,"open_issues_count":14,"forks_count":0,"subscribers_count":2,"default_branch":"master","last_synced_at":"2023-03-07T14:42:43.275Z","etag":null,"topics":["declarative","framework","webaudio"],"latest_commit_sha":null,"homepage":"https://flow-lang.github.io","language":"JavaScript","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/flow-lang.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2019-07-19T08:49:37.000Z","updated_at":"2021-09-28T18:22:48.000Z","dependencies_parsed_at":"2023-02-06T17:46:36.687Z","dependency_job_id":null,"html_url":"https://github.com/flow-lang/flow-framework","commit_stats":null,"previous_names":[],"tags_count":null,"template":null,"template_full_name":null,"purl":"pkg:github/flow-lang/flow-framework","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/flow-lang%2Fflow-framework","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/flow-lang%2Fflow-framework/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/flow-lang%2Fflow-framework/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/flow-lang%2Fflow-framework/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/flow-lang","download_url":"https://codeload.github.com/flow-lang/flow-framework/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/flow-lang%2Fflow-framework/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":35413538,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-07-13T02:00:06.543Z","response_time":119,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"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":["declarative","framework","webaudio"],"created_at":"2026-07-13T06:33:03.373Z","updated_at":"2026-07-13T06:33:04.018Z","avatar_url":"https://github.com/flow-lang.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# The Flow Framework\n\u003e An Elm-inspired framework for Web Audio applications. Flow is positioned as an\nalternative to frameworks like React, with a tight integration with the Web\nAudio API.\n\n---\n\n![](/assets/github-repo-splash.png)\n\n## Motivation\nA number of projects exist to provide complete frameworks for building Web Audio\napplications such as BRAID\u003csup name=\"braid-ref\"\u003e[1](#braid)\u003c/sup\u003e,\nWAAX\u003csup name=\"waax-ref\"\u003e[2](#waax)\u003c/sup\u003e, and\nFlocking\u003csup name=\"flocking-ref\"\u003e[3](#flocking)\u003c/sup\u003e. All of these examples\nencourage a tight coupling between the UI and the audio graph:\n\n- BRAID relies on global variables and callbacks attached to UI elements to\ndirectly manipulate audio nodes.\n- WAAX uses Web Components to provide a `.connect` method for UI elements. These\nelements can be connected directly to audio node parameters to control their \nvalue.\n- Similarly, Flocking UI elements directly manipulate audio params.\n\nWhile such an approach is acceptable for small applications. It becomes increasingly\ndifficult to manage application and audio graph state as an application grows. \nRelying on global variables as done in BRAID is simply not a scalalbe solution\nfor serious applications, and the tight coupling between UI and audio found in\nWAAX and Flocking can make it difficult to determine how and when application\nstate is being changed.\n\nFlow takes a different approach in line with more modern frontend frameworks. We\nargue for strict separation of audio and view code, instead choosing to generate\nboth from a single, immutable model. This prevents one going out of sync with\nthe other while also ensuring that a refactor of the view won't impact how the \napp handles audio.\n\nFlow also provides a declarative API for Web Audio development that is\nsignficantly clearer than the vanilla Web Audio API. Connections, for example,\nare much more clearly expressed with Flow's audio library:\n\n```javascript\n// Flow\nosc([], [\n  gain([], [\n    dac()\n  ])\n])\n\n// Web Audio API\nconst osc = context.createOscillator()\nconst amp = context.createGain()\n\nosc.connect(amp)\namp.connect(context.destination)\n```\n\nIt is also much easier to define reusable _sub graphs_ such as combining the\nabove oscillator and gain node into a reusable synth node:\n\n```javascript\n// Flow\nconst synth = (freq, vol, connections) =\u003e\n  osc([ Prop.frequency(freq) ], [\n    gain([ Prop.gain(vol) ], [\n      ...connections\n    ])\n  ])\n\n// Web Audio API\nconst synth = (freq, vol) =\u003e {\n  const osc = context.createOscillator()\n  const amp = context.createGain()\n\n  osc.frequency.value = freq\n  amp.gain.value = vol\n\n  osc.connect(amp)\n\n  return {\n    connect (node) {\n      amp.connect(node)\n    }\n  }\n}\n```\n\n## Features\n\n- [x] Declarative audio API\n- [x] \"Anonymous\" audio nodes\n- [x] Clear unidirectional dataflow thanks to the MVU architecture\n- [x] Predictable model updates thanks to Actions and single update function\n- [x] Managed side effects thanks to Effects\n- [x] Plugin system allowing custom events\n- [ ] TODO: Time travel debugger\n- [ ] TODO: Plugin system allowing custom audio nodes and DOM elements\n\n## Installation\nFirst, install the library from npm:\n\n```\nnpm i @flow-lang/framework\n````\n\nThen make sure your HTMl has an element for Flow to mount to:\n\n```html\n\u003cbody\u003e\n  \u003cdiv id=\"app\"\u003e\u003c/div\u003e\n  \u003cscript src=\"main.js\"\u003e\u003c/script\u003e\n\u003c/body\u003e\n```\n\nFinally, create a `main.js` to init your application:\n\n```javascript\nimport { Program, DOM, Audio, Music } from '@flow-lang/framework'\n\n...\n\nconst App = Program.instrument(init, update, audio, view listen)\n\nApp.use(DOM.Event)\nApp.use(Audio.Event)\nApp.start({\n  root: document.querySelector('#app'),\n  context: new AudioContext()\n})\n```\n\n## Docs and Reference\nAn official guide, complete with examples and a full API reference can be found\n[here](https://flow-lang.github.io/).\n\n## Example\nIt's not uncommon for frameworks to have a simple counter application to\ndemonstrate how they work. Flow is no different, but this time it's a counter\nwith an audio twist!\n\nFirst, import everything we need from Flow\n\n```javascript\nimport { Program, DOM, Audio, Music } from '@flow-lang/framework'\n```\n\nThe init function is called once when we call `App.start` and is used to generate\nthe initial model for our application. For our counter app we need to keep track\nof the current count, and we're also going to define some notes of a chord to\ntrigger.\n\nThe `Music.Note` library contains some handy utilities for converting to and from\ndifferent formats. Here we're converting note names to frequency values.\n\n```javascript\nfunction init () {\n  const voices = [ 'C3', 'E3', 'G3', 'B3', 'D4', 'G4', 'B4' ]\n\n  return {\n    count: 0,\n    voices: voices.map(Music.Note.ntof)\n  }\n}\n```\n\nUpdate is called by the runtime whenever a new model needs to be created. Here,\nwe switch on our two actions (Increment and Decrement) to increase or decrease\nthe counter.\n\nBecause we're also going to use the count to trigger notes in a\nchord we need to make sure the counter doesn't dip below 0 or increase above the\nnumber of notes we have defined.\n\n```javascript\nconst Increment = 0\nconst Decrement = 1\n\nfunction update ({ action }, model) {\n  switch (action) {\n    case Increment:\n      if (model.count \u003c model.voices.length) {\n        return { ...model, count: model.count + 1 }\n      } else {\n        return { ...model }\n      }\n\n    case Decrement:\n      if (model.count \u003e 0) {\n        return { ...model, count: model.count - 1 }\n      } else {\n        return { ...model }\n      }\n  }\n}\n```\n\nWe've declared our two actions as constants in a sort of enum but this isn't\nnecessary. You may wish to use strings instead of numbers (perhaps for better\ndebugging) or not to use constants at all.\n\nThe audio function is called every time the model updates. Here, we map over\nthe voices array and map each note into an oscillator. By comparing the current\nindex to the model's count we can conditionally turn off some voices by setting\nthe gain to 0.\n\n```javascript\nfunction audio ({ count, voices ) {\n  return voices.map((note, i) =\u003e \n    Audio.Node.oscillator([ Audio.Property.frequency(note) ], [\n      Audio.Node.gain([ Audio.Property.gain(i \u003c count ? 0.1 : 0) ], [\n        Audio.Node.dac()\n      ])\n    ])  \n  )\n}\n```\n\nAs with the audio function, the view function is also called whenever the model\nchanges. \n\n```javascript\nfunction view ({ count, voices }) {\n  return DOM.Element.div([], [\n    DOM.Element.button([ DOM.Attribute.id('incr') ], [ '+' ]),\n    DOM.Element.div([], [ count.toString() ]),\n    DOM.Element.button([ DOM.Attribute.id('decr') ], [ '-' ])\n  ])\n}\n```\n\nInstead of attaching event listeners to DOM nodes directly, the listen function\nserves as the single place to define all event listeners. Here we attach click\nevent listeners to the two buttons and return an appropriate action.\n\n```javascript\nfunction listen (model) {\n  return [\n    DOM.Event.click('#incr', e =\u003e ({ action: Increment })),\n    DOM.Event.click('#decr', e =\u003e ({ action: Decrement })),\n  ]\n}\n```\n\nWith everything defined we can create a new application. The `instrument` program\nis a complete Flow application with an audio, view, and listen function. \n\nThe Flow runtime needs to know how to setup and handle different types of events,\nso we tell our application to use the DOM.Event plugin.\n\nFinally we start the application, supplying a DOM node to inject our view into\nand an audio context used to create our audio nodes.\n\n```javascript\nconst App = Program.instrument(init, update, audio, view, listen)\n\nApp.use(DOM.Event)\nApp.start({\n  root: document.querySelector('#app'),\n  context: new AudioContext()\n})\n```\n\nYou can see the code for this example as one piece in\n[examples/counter/](/examples/counter/). Along with this counter example there\nare a few other example applications including:\n\n- [x] A step sequencer: [source](/examples/step-sequencer)\n- [ ] TODO A polyphonic synth: ~~[source]()~~\n\n## References\n- [\u003ca name=\"braid\" href=\"#braid-ref\"\u003e1\u003c/a\u003e] BRAID: : A Web Audio Instrument Builder with Embedded\nCode Blocks – [paper](https://pdfs.semanticscholar.org/d92e/9fe43966b903c514613feaf281d2a40a6cc0.pdf)\n- [\u003ca name=\"waax\" href=\"#waax-ref\"\u003e2\u003c/a\u003e] WAAX: Web Audio API eXtension – [paper](http://nime.org/proceedings/2013/nime2013_119.pdf)\n- [\u003ca name=\"flocking\" href=\"#flocking-ref\"\u003e3\u003c/a\u003e] Flocking: A Framework for Declarative Music-Making on the Web – [paper](https://pdfs.semanticscholar.org/bcbf/b66bc4ced14beafcf9e7463c37692faa9a29.pdf)","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fflow-lang%2Fflow-framework","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fflow-lang%2Fflow-framework","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fflow-lang%2Fflow-framework/lists"}