I'm using the Tone.js library to build a (music) sequencer in Vue+Vuex, and have encountered this problem:

Individual music tracks of the sequencer are created dynamically, so I'm storing objects for them on the state, including a reference to their synthesizers (ie: new Tone.Synth()) ... when these synths are played (my call, from App.vue: this.synths[index].triggerAttackRelease(pitch, '8n', time), they start but don't stop and depending on the tempo, it can overflow the stack. Basically, it seems that Tone.js is attempting to directly mutate the state, which causes mayhem... ie: strict mode triggers a warning: vue.esm.js?efeb:591 [Vue warn]: Error in callback for watcher "function () { return this._data.$$state }": "Error: [vuex] Do not mutate vuex store state outside mutation handlers."

I'm pretty confident I've diagnosed the problem correctly here, but the solution seems a bit circuitous: create an array in my App.vue state to house the synths, and then update it in parallel with the track information on the Vuex state. The thing that strikes me as particularly problematic here is that I have Save/Load functions (using LocalStorage) which essentially copy/repopulate the Vuex state; to these I would have to add a function which also updates these synths on the main app. Doable, but it feels indirect and weird.

Specifically I'm wondering: Is there a better way? What sort of approaches are best for this sort of situation? What concepts or principles might I need to know about to write good code here? And of course, if you think I've not correctly diagnosed the problem, please let me know about that too.

Finally - I should clarify that I'm a hobbyist, not a professional, and due to time constraints I am really pretty wedded to Vue/Vuex for the time being.


This may not be much help because the answer is in React + Redux, but it seems like a nice way of architecting an application, so maybe you can get the abstract idea and work it to your advantage. http://joesul.li/van/react-and-web-audio/

If you treat the synth like a thing that doesn't mutate the state, but only responds to updates in state. I don't know Vue, but I think you could 'listen' for changes in your component's beforeUpdate function right? https://alligator.io/vuejs/component-lifecycle/ - so all your stuff processes changes in there?

I don't know if it helps, but my implementation for the processEvent described by the article is here: https://github.com/glenpike/microsynth/blob/master/src/components/WebAudioSynth/WebAudioSynth.js#L187

My system uses Redux which maps the changes in state (the Redux Store) to 'props' using a Container. The events get passed in by my componentWillUpdate function, which is possibly akin to your beforeUpdate function in Vue

Hopefully that's not confusing. I did have similar errors to you with complaints about things happening within the 'flow' of Redux, which sounds like what's happening to you with Vuex as they're both 'Flux' implementations - when I got my stuff inside the correct lifecycle functions that seemed to work.

  • Hey thanks, looking forward to checking this out. Some of your suggestions (ie: 'listening' for state change) I've implemented, others I need to study before I understand them! :)
    – drenl
    Jun 22 '18 at 3:32

It sounds like you were asking Vue to convert these Tone synth objects into reactive objects, which means it descends into them and wraps every property into a getter and setter so it can keep track of any mutations. When you call a method on a synth, it modifies its own properties, creating a whole chain of unnecessary mutations in your Vuex data store.

A simple solution is to keep those Tone objects out of your Vuex data store. You can import them, instantiate them, and call methods on them from within a component, and even bind it to the component, but just make sure they're not made reactive (you can, e.g., instantiate/bind them to the component in the created lifecycle hook, but make sure not to include their names as property names in the data object).

As an example, let's say you want some range inputs to control parameters of a Tone Synth. You could v-bind those inputs to computed setters in your component to automatically update your Vuex Store, and also watch those same properties to perform Tone object updates.

The larger point here is you probably shouldn't track code you didn't write unless you have sufficient reason to because who knows how much extra work it could generate. Additionally, in this case, Tone objects are the boundaries of your reactivity systems in that nothing in your app code is dependent on them. Their job is to communicate with the browser internals and the surrounding operating system to produce sound.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.