From d42aa4cffd4d188fcd5eedae7352584d29dcfa6b Mon Sep 17 00:00:00 2001 From: Richard Davey Date: Fri, 15 Dec 2017 04:06:50 +0000 Subject: [PATCH 01/10] Updated Container class and factory --- v3/src/gameobjects/container/Container.js | 147 ++++++++++++++++-- .../gameobjects/container/ContainerFactory.js | 7 +- 2 files changed, 137 insertions(+), 17 deletions(-) diff --git a/v3/src/gameobjects/container/Container.js b/v3/src/gameobjects/container/Container.js index dda64d946..71da0e997 100644 --- a/v3/src/gameobjects/container/Container.js +++ b/v3/src/gameobjects/container/Container.js @@ -1,16 +1,24 @@ var Class = require('../../utils/Class'); +var Components = require('../components'); var DataProxy = require('../components/DataProxy'); -var List = require('../../structs/List'); +var DisplayList = require('../../scene/plugins/DisplayList'); +var TransformMatrix = require('../components/TransformMatrix'); var Container = new Class({ - Extends: List, + // Extends: DisplayList, + + // Mixins: [ + // Components.Visible, + // SpriteRender + // ], initialize: function Container (scene, x, y) { - List.call(this, this); + // this.list = children + // DisplayList.call(this, scene); this.scene = scene; @@ -18,19 +26,24 @@ var Container = new Class({ this.name = ''; + // Likely swap for a ProcessQueue to make it iteration safe + this.list = []; + this.active = true; this.data = new DataProxy(scene, this); + this._depth = 0; + + this._transform = new TransformMatrix(); + this._x = x; this._y = y; - this._z = 0; - this._w = 0; - this._scaleX = 1; this._scaleY = 1; this._rotation = 0; - this._depth = 0; + + this._visible = true; }, x: { @@ -42,14 +55,11 @@ var Container = new Class({ set: function (value) { - var diff = this._x - value; - this._x = value; - // Update all children for (var i = 0; i < this.list.length; i++) { - this.list[i].x += diff; + // this.list[i].depth = value; } } @@ -64,19 +74,125 @@ var Container = new Class({ set: function (value) { - var diff = this._y - value; - this._y = value; - // Update all children for (var i = 0; i < this.list.length; i++) { - this.list[i].y += diff; + // this.list[i].depth = value; } } }, + depth: { + + get: function () + { + return this._depth; + }, + + set: function (value) + { + this._depth = value; + + for (var i = 0; i < this.list.length; i++) + { + this.list[i].depth = value; + } + } + + }, + + setDepth: function (value) + { + if (value === undefined) { value = 0; } + + this.depth = value; + + return this; + }, + + visible: { + + get: function () + { + return this._visible; + }, + + set: function (value) + { + if (value) + { + this._visible = true; + } + else + { + this._visible = false; + } + + for (var i = 0; i < this.list.length; i++) + { + this.list[i].visible = value; + } + } + + }, + + setVisible: function (value) + { + this.visible = value; + + return this; + }, + + add: function (child) + { + // Don't allow containers to be added + + // Is child already in this container? + + if (this.getIndex(child) === -1 && child.parent !== this) + { + // No, good ... + this.scene.sys.updateList.remove(child); + + if (child.parent) + { + child.parent.remove(child); + } + + child.parent = this; + + this.list.push(child); + + // this.scene.sys.sortChildrenFlag = true; + } + + return child; + }, + + remove: function (child) + { + var index = this.list.indexOf(child); + + if (index !== -1) + { + // Not iteration safe - use ProcessQueue instead? + this.list.splice(index, 1); + + child.parent = null; + + // this.scene.sys.sortChildrenFlag = true; + } + + return child; + }, + + preUpdate: function (time, delta) + { + // iterate children and call preUpdate on them, as they won't be part of the Scenes updateList + }, + setActive: function (value) { this.active = value; @@ -105,7 +221,6 @@ var Container = new Class({ destroy: function () { - } }); diff --git a/v3/src/gameobjects/container/ContainerFactory.js b/v3/src/gameobjects/container/ContainerFactory.js index 3d6c2e65a..9e631680a 100644 --- a/v3/src/gameobjects/container/ContainerFactory.js +++ b/v3/src/gameobjects/container/ContainerFactory.js @@ -11,5 +11,10 @@ var GameObjectFactory = require('../../scene/plugins/GameObjectFactory'); GameObjectFactory.register('container', function (x, y) { - return new Container(this.scene, x, y); + var container = new Container(this.scene, x, y); + + // this.displayList.add(container); + this.updateList.add(container); + + return container; }); From 61769526d46264ca48372b33f58dd12d41b1ba1f Mon Sep 17 00:00:00 2001 From: Richard Davey Date: Fri, 15 Dec 2017 04:07:16 +0000 Subject: [PATCH 02/10] Skip children that won't render --- v3/src/renderer/webgl/WebGLRenderer.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/v3/src/renderer/webgl/WebGLRenderer.js b/v3/src/renderer/webgl/WebGLRenderer.js index 3f057a9bd..9bee6e6e0 100644 --- a/v3/src/renderer/webgl/WebGLRenderer.js +++ b/v3/src/renderer/webgl/WebGLRenderer.js @@ -459,6 +459,11 @@ var WebGLRenderer = new Class({ { var child = list[index]; + if (!child.willRender()) + { + continue; + } + if (child.blendMode !== this.blendMode) { this.setBlendMode(child.blendMode); From 8632fe541015a0609d827d933bb07a1d65e80d61 Mon Sep 17 00:00:00 2001 From: Richard Davey Date: Fri, 15 Dec 2017 04:07:32 +0000 Subject: [PATCH 03/10] Inlined the depth sort --- v3/src/scene/local/Systems.js | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/v3/src/scene/local/Systems.js b/v3/src/scene/local/Systems.js index 2f0cd1292..5b245c79f 100644 --- a/v3/src/scene/local/Systems.js +++ b/v3/src/scene/local/Systems.js @@ -27,6 +27,8 @@ var Systems = new Class({ this.config = config; this.settings = Settings.create(config); + this.renderList = []; + this.sortChildrenFlag = false; // Set by the GlobalSceneManager @@ -151,7 +153,13 @@ var Systems = new Class({ return; } - this.depthSort(); + // inlined to avoid branching + if (this.sortChildrenFlag) + { + StableSort.inplace(this.displayList.list, this.sortZ); + + this.sortChildrenFlag = false; + } this.cameras.render(renderer, this.displayList, interpolation); }, From 912b318d3e75f35c37cc967bebb8d3312d08d29a Mon Sep 17 00:00:00 2001 From: Richard Davey Date: Fri, 15 Dec 2017 04:07:45 +0000 Subject: [PATCH 04/10] Formating --- v3/src/gameobjects/sprite/Sprite.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/v3/src/gameobjects/sprite/Sprite.js b/v3/src/gameobjects/sprite/Sprite.js index 32e14a4dc..3b758fcf8 100644 --- a/v3/src/gameobjects/sprite/Sprite.js +++ b/v3/src/gameobjects/sprite/Sprite.js @@ -1,7 +1,6 @@ - var Class = require('../../utils/Class'); -var GameObject = require('../GameObject'); var Components = require('../components'); +var GameObject = require('../GameObject'); var SpriteRender = require('./SpriteRender'); var Sprite = new Class({ From 76f27ed706f7d37d6b0aefea0fa8494d78f3f055 Mon Sep 17 00:00:00 2001 From: Richard Davey Date: Fri, 15 Dec 2017 04:08:05 +0000 Subject: [PATCH 05/10] Added parent, setParent and fixed destroy --- v3/src/gameobjects/GameObject.js | 30 +++++++++++++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) diff --git a/v3/src/gameobjects/GameObject.js b/v3/src/gameobjects/GameObject.js index 390f0fa43..faeddef62 100644 --- a/v3/src/gameobjects/GameObject.js +++ b/v3/src/gameobjects/GameObject.js @@ -35,6 +35,16 @@ var GameObject = new Class({ */ this.scene = scene; + /** + * The parent Container of this Game Object, if any. + * Game Objects do not have to belong to Containers and can exist on the + * Display List on their own. + * + * @property {Phaser.GameObject.Container} parent + * @protected + */ + this.parent = null; + /** * A textual representation of this Game Object, i.e. `sprite`. * Used internally by Phaser but is available for your own custom classes to populate. @@ -149,6 +159,24 @@ var GameObject = new Class({ return this; }, + // Testing: Add this Game Object to a Container parent. + // Can only belong to one Container at once. + // The Container takes over its transform and depth management. + // Call this method with no arguments to remove it from a parent. + setParent: function (newParent) + { + if (newParent) + { + newParent.add(this); + } + else if (this.parent) + { + this.parent.remove(this); + } + + return this; + }, + /** * This is a quick chainable alias to the `DataProxy.set` method. * It allows you to set a key and value in this Game Objects data store. @@ -269,7 +297,7 @@ var GameObject = new Class({ this.scene.sys.sortChildrenFlag = true; this.active = false; - this.Visible = false; + this.visible = false; this.data = undefined; From d999151d3059584de6567bc5d485c9e409ff1e5b Mon Sep 17 00:00:00 2001 From: Richard Davey Date: Fri, 15 Dec 2017 04:08:25 +0000 Subject: [PATCH 06/10] Added in start of the support for a parent transform --- v3/src/gameobjects/components/Transform.js | 133 +++++++++++++++++++-- 1 file changed, 121 insertions(+), 12 deletions(-) diff --git a/v3/src/gameobjects/components/Transform.js b/v3/src/gameobjects/components/Transform.js index fa81be1e7..8013e68a5 100644 --- a/v3/src/gameobjects/components/Transform.js +++ b/v3/src/gameobjects/components/Transform.js @@ -14,25 +14,78 @@ var Transform = { _scaleY: 1, _rotation: 0, _depth: 0, + _dirty: false, + _world: { a: 1, b: 0, c: 0, d: 1, tx: 0, ty: 0, sr: 0, cr: 0 }, // public properties / methods - x: 0, - y: 0, - z: 0, - w: 0, + // These are world coordinate values. - depth: { + // If Game Object is a child of a Container, then you can modify its local position (relative to the Container) + // by setting `localX`, `localY`, etc (or changing x/y directly, but remember the values given here are world based). + // Changes to the parent Container are instantly reflected in the world coords here (x,y, etc) + + _x: 0, + _y: 0, + _z: 0, + _w: 0, + + x: { get: function () { - return this._depth; + return this._x; }, set: function (value) { - this.scene.sys.sortChildrenFlag = true; - this._depth = value; + this._x = value; + this._dirty = true; + } + + }, + + y: { + + get: function () + { + return this._y; + }, + + set: function (value) + { + this._y = value; + this._dirty = true; + } + + }, + + z: { + + get: function () + { + return this._z; + }, + + set: function (value) + { + this._z = value; + this._dirty = true; + } + + }, + + w: { + + get: function () + { + return this._w; + }, + + set: function (value) + { + this._w = value; + this._dirty = true; } }, @@ -47,6 +100,7 @@ var Transform = { set: function (value) { this._scaleX = value; + this._dirty = true; if (this._scaleX === 0) { @@ -70,6 +124,7 @@ var Transform = { set: function (value) { this._scaleY = value; + this._dirty = true; if (this._scaleY === 0) { @@ -108,9 +163,29 @@ var Transform = { { // value is in radians this._rotation = WrapAngle(value); + + this._world.sr = Math.sin(this._rotation); + this._world.cr = Math.cos(this._rotation); + + this._dirty = true; } }, + depth: { + + get: function () + { + return this._depth; + }, + + set: function (value) + { + this.scene.sys.sortChildrenFlag = true; + this._depth = value; + } + + }, + setPosition: function (x, y, z, w) { if (x === undefined) { x = 0; } @@ -118,10 +193,12 @@ var Transform = { if (z === undefined) { z = 0; } if (w === undefined) { w = 0; } - this.x = x; - this.y = y; - this.z = z; - this.w = w; + this._x = x; + this._y = y; + this._z = z; + this._w = w; + + this._dirty = true; return this; }, @@ -180,6 +257,38 @@ var Transform = { this.depth = value; return this; + }, + + updateTransform: function () + { + if (!this.parent || !this._dirty) + { + return; + } + + var tx = this._x; + var ty = this._y; + var world = this._world; + + var parent = this.parent.world; + + var a = world.cr * this._scaleX; + var b = world.sr * this._scaleX; + var c = -world.sr * this._scaleY; + var d = world.cr * this._scaleY; + + world.a = (a * parent.a) + (b * parent.c); + world.b = (a * parent.b) + (b * parent.d); + world.c = (c * parent.a) + (d * parent.c); + world.d = (c * parent.b) + (d * parent.d); + + // this._worldRotation = Math.atan2(-this.world.c, this.world.d); + + world.tx = (tx * parent.a) + (ty * parent.c) + parent.tx; + world.ty = (tx * parent.b) + (ty * parent.d) + parent.ty; + + // this._worldScaleX = this._scaleX * Math.sqrt((world.a * world.a) + (world.c * world.c)); + // this._worldScaleY = this._scaleY * Math.sqrt((world.b * world.b) + (world.d * world.d)); } }; From b4dcbc428fd33d45e6421a1131913f3a16be1626 Mon Sep 17 00:00:00 2001 From: Richard Davey Date: Fri, 15 Dec 2017 04:08:55 +0000 Subject: [PATCH 07/10] Backface cull can be enabled per type --- v3/src/geom/mesh/Mesh.js | 56 +++++++++++++++++++++++++++++++++++----- 1 file changed, 50 insertions(+), 6 deletions(-) diff --git a/v3/src/geom/mesh/Mesh.js b/v3/src/geom/mesh/Mesh.js index 6ea4e2470..1c23bfb62 100644 --- a/v3/src/geom/mesh/Mesh.js +++ b/v3/src/geom/mesh/Mesh.js @@ -26,7 +26,8 @@ var Mesh = new Class({ this.fillColor = 0x00ff00; this.fillAlpha = 1; - this.backfaceCull = true; + this.backfaceCullStroke = false; + this.backfaceCullFill = false; this.points = []; @@ -58,6 +59,8 @@ var Mesh = new Class({ graphics.fillStyle(this.fillColor, this.fillAlpha); + // Depth Sort + for (var m = 0; m < this.data.models.length; m++) { var model = this.data.models[m]; @@ -77,6 +80,18 @@ var Mesh = new Class({ } } + + // if (f % 2) + // { + // graphics.fillStyle(0xff0000, this.fillAlpha); + // } + // else + // { + // graphics.fillStyle(0xffffff, this.fillAlpha); + // } + + + }, fillTriangle: function (graphics, face) @@ -92,7 +107,7 @@ var Mesh = new Class({ this.project(graphics, b, verts[face.vertices[1].vertexIndex], world); this.project(graphics, c, verts[face.vertices[2].vertexIndex], world); - if (this.backfaceCull && !this.isBackFacing(a, b, c)) + if (!this.backfaceCullFill || (this.backfaceCullFill && !this.isBackFaceTriangle(a, b, c))) { graphics.fillTriangle(a.x, a.y, b.x, b.y, c.x, c.y); } @@ -112,7 +127,10 @@ var Mesh = new Class({ this.project(graphics, points[i], verts[face.vertices[i].vertexIndex], world); } - graphics.fillPoints(points, true, size); + if (!this.backfaceCullFill || (this.backfaceCullFill && this.isBackFacePoly(points, size))) + { + graphics.fillPoints(points, true, size); + } }, stroke: function (graphics) @@ -159,7 +177,7 @@ var Mesh = new Class({ this.project(graphics, b, verts[face.vertices[1].vertexIndex], world); this.project(graphics, c, verts[face.vertices[2].vertexIndex], world); - if (this.backfaceCull && !this.isBackFacing(a, b, c)) + if (!this.backfaceCullStroke || (this.backfaceCullStroke && !this.isBackFaceTriangle(a, b, c))) { graphics.strokeTriangle(a.x, a.y, b.x, b.y, c.x, c.y); } @@ -179,7 +197,10 @@ var Mesh = new Class({ this.project(graphics, points[i], verts[face.vertices[i].vertexIndex], world); } - graphics.strokePoints(points, true, size); + if (!this.backfaceCullStroke || (this.backfaceCullStroke && this.isBackFacePoly(points, size))) + { + graphics.strokePoints(points, true, size); + } }, // local is a Vec2 that is changed in place (so not returned) @@ -198,7 +219,7 @@ var Mesh = new Class({ local.y = -point.y * h + h / 2 >> 0; }, - isBackFacing: function (a, b, c) + isBackFaceTriangle: function (a, b, c) { var ax = c.x - a.x; var ay = c.y - a.y; @@ -211,6 +232,29 @@ var Mesh = new Class({ return (result >= 0); }, + isBackFacePoly: function (points, endIndex) + { + var area = 0; + + for (var i = 0; i < endIndex; i++) + { + j = (i + 1) % endIndex; + + area += points[i].x * points[j].y; + area -= points[j].x * points[i].y; + } + + return (area / 2); + }, + + setBackfaceCull: function (stroke, fill) + { + this.backfaceCullStroke = stroke; + this.backfaceCullFill = fill; + + return this; + }, + setPosition: function (x, y, z) { if (x === undefined) { x = 0; } From 2c9c220f58fcd0d55484c45d1d925844964dc747 Mon Sep 17 00:00:00 2001 From: Richard Davey Date: Fri, 15 Dec 2017 04:09:20 +0000 Subject: [PATCH 08/10] Container render --- .../gameobjects/container/ContainerRender.js | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 v3/src/gameobjects/container/ContainerRender.js diff --git a/v3/src/gameobjects/container/ContainerRender.js b/v3/src/gameobjects/container/ContainerRender.js new file mode 100644 index 000000000..5c1970898 --- /dev/null +++ b/v3/src/gameobjects/container/ContainerRender.js @@ -0,0 +1,19 @@ +var renderWebGL = require('../../utils/NOOP'); +var renderCanvas = require('../../utils/NOOP'); + +if (WEBGL_RENDERER) +{ + renderWebGL = require('./ContainerWebGLRenderer'); +} + +if (CANVAS_RENDERER) +{ + renderCanvas = require('./ContainerCanvasRenderer'); +} + +module.exports = { + + renderWebGL: renderWebGL, + renderCanvas: renderCanvas + +}; From 3b6f3ef3b3ca5e2a70cd66f90857c363ba9361f7 Mon Sep 17 00:00:00 2001 From: Felipe Alfonso Date: Fri, 15 Dec 2017 13:03:55 -0300 Subject: [PATCH 09/10] RenderPass fix --- v3/src/gameobjects/components/Shader.js | 12 ++++++++++++ v3/src/gameobjects/components/index.js | 1 + v3/src/gameobjects/renderpass/RenderPass.js | 4 ++++ .../webgl/renderers/shapebatch/ShapeBatch.js | 2 +- 4 files changed, 18 insertions(+), 1 deletion(-) create mode 100644 v3/src/gameobjects/components/Shader.js diff --git a/v3/src/gameobjects/components/Shader.js b/v3/src/gameobjects/components/Shader.js new file mode 100644 index 000000000..43a9baba9 --- /dev/null +++ b/v3/src/gameobjects/components/Shader.js @@ -0,0 +1,12 @@ +var Shader = { + + shader: null, + + setShader: function (shader) + { + this.shader = shader; + } + +}; + +module.exports = Shader; \ No newline at end of file diff --git a/v3/src/gameobjects/components/index.js b/v3/src/gameobjects/components/index.js index b4b43070f..b124376b0 100644 --- a/v3/src/gameobjects/components/index.js +++ b/v3/src/gameobjects/components/index.js @@ -14,6 +14,7 @@ module.exports = { RenderTarget: require('./RenderTarget'), ScaleMode: require('./ScaleMode'), ScrollFactor: require('./ScrollFactor'), + Shader: require('./Shader'), Size: require('./Size'), Texture: require('./Texture'), Tint: require('./Tint'), diff --git a/v3/src/gameobjects/renderpass/RenderPass.js b/v3/src/gameobjects/renderpass/RenderPass.js index 8e03a6248..5f774d5d6 100644 --- a/v3/src/gameobjects/renderpass/RenderPass.js +++ b/v3/src/gameobjects/renderpass/RenderPass.js @@ -134,6 +134,8 @@ var RenderPass = new Class({ if (gl && !renderer.contextLost) { + //gameObject.setShader(this.passShader); + gameObject.setRenderTarget(this.passRenderTarget); gameObject.renderWebGL(renderer, gameObject, 0.0, camera); for (var key in this.textures) @@ -145,6 +147,8 @@ var RenderPass = new Class({ gl.activeTexture(gl.TEXTURE0); } renderer.currentRenderer.flush(this.passShader, this.passRenderTarget); + gameObject.setRenderTarget(null); + //gameObject.setShader(null); } }, diff --git a/v3/src/renderer/webgl/renderers/shapebatch/ShapeBatch.js b/v3/src/renderer/webgl/renderers/shapebatch/ShapeBatch.js index 7592861e8..f201b2488 100644 --- a/v3/src/renderer/webgl/renderers/shapebatch/ShapeBatch.js +++ b/v3/src/renderer/webgl/renderers/shapebatch/ShapeBatch.js @@ -101,7 +101,7 @@ var ShapeBatch = new Class({ }, flush: function (shader, renderTarget) - { + { var gl = this.glContext; var vertexDataBuffer = this.vertexDataBuffer; From 09afe0be34c19a98329e9b2d0a098e398be84cd5 Mon Sep 17 00:00:00 2001 From: orblazer Date: Sat, 16 Dec 2017 02:47:30 +0100 Subject: [PATCH 10/10] Fix deprecated WebAudio value change on FX --- v3/src/sound/dynamic/FX.js | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/v3/src/sound/dynamic/FX.js b/v3/src/sound/dynamic/FX.js index 7fa40f446..6b1b28197 100644 --- a/v3/src/sound/dynamic/FX.js +++ b/v3/src/sound/dynamic/FX.js @@ -60,15 +60,15 @@ var FX = new Class({ // Set the values - this.volume.gain.value = this.volumeValue; - + this.volume.gain.setTargetAtTime(this.volumeValue, 0, 0.01); + if (!ctx.createStereoPanner) { this.pan.setPosition(this.panValue, 0, 1 - Math.abs(this.panValue)); } else { - this.pan.pan.value = this.panValue; + this.pan.pan.setTargetAtTime(this.panValue, 0, 0.01); } // Create an oscillator, gain and pan nodes, and connect them together to the destination @@ -84,14 +84,14 @@ var FX = new Class({ if (this.randomValue > 0) { - oscillator.frequency.value = Between( + oscillator.frequency.setTargetAtTime(Between( this.frequencyValue - this.randomValue / 2, this.frequencyValue + this.randomValue / 2 - ); + ), 0, 0.01); } else { - oscillator.frequency.value = this.frequencyValue; + oscillator.frequency.setTargetAtTime(this.frequencyValue, 0, 0.01); } // Apply effects @@ -149,7 +149,7 @@ var FX = new Class({ fadeIn: function (volume) { - volume.gain.value = 0; + volume.gain.setTargetAtTime(0, this.audioContext.currentTime, 0.01); volume.gain.linearRampToValueAtTime(0, this.audioContext.currentTime + this.wait); @@ -182,12 +182,12 @@ var FX = new Class({ // Set the node values - feedback.gain.value = this.echoFeedback; - delay.delayTime.value = this.echoDelay; + feedback.gain.setTargetAtTime(this.echoFeedback, 0, 0.01); + delay.delayTime.setTargetAtTime(this.echoDelay, 0, 0.01); if (this.echoFilter) { - filter.frequency.value = this.echoFilter; + filter.frequency.setTargetAtTime(this.echoFilter, 0, 0.01); } // Create the delay feedback loop (with optional filtering) @@ -245,8 +245,8 @@ var FX = new Class({ var d2Volume = ctx.createGain(); // Set the volume to the `volumeValue` - d1Volume.gain.value = this.volumeValue; - d2Volume.gain.value = this.volumeValue; + d1Volume.gain.setTargetAtTime(this.volumeValue, 0, 0.01); + d2Volume.gain.setTargetAtTime(this.volumeValue, 0, 0.01); // Connect the oscillators to the gain and destination nodes d1.connect(d1Volume); @@ -261,8 +261,8 @@ var FX = new Class({ // Make the two oscillators play at frequencies above and below the main sound's frequency. // Use whatever value was supplied by the `dissonance` argument - d1.frequency.value = this.frequencyValue + this.dissonance; - d2.frequency.value = this.frequencyValue - this.dissonance; + d1.frequency.setTargetAtTime(this.frequencyValue + this.dissonance, 0, 0.01); + d2.frequency.setTargetAtTime(this.frequencyValue - this.dissonance, 0, 0.01); // Fade in / out, pitch bend and play the oscillators to match the main sound if (this.attack > 0)