I have this script in the header of an HTML file. It works fine with the exception of the line delay.connect(Tone.context.destination); midway down that theoretically adds layering of the sounds in the DuoSynth by starting one of them on a delay.
When it is not commented out, I get this error in the console: Uncaught TypeError: Failed to execute 'connect' on 'AudioNode': No function was found that matched the signature provided.
What am I doing wrong?
<script>
function makeSynth() {
let envelope = {
attack: 0.1,
release: 4,
releaseCurve: 'exponential'
};
let filterEnvelope = {
baseFrequency: 440,
octaves: 3,
attack: 0,
decay: 0,
release: 1000
};
return new Tone.DuoSynth({
harmonicity: .5,
detune : 5000,
volume: -20,
voice0: {
oscillator: { type: 'triangle' },
envelope,
filterEnvelope
},
voice1: {
oscillator: { type: 'sine' },
envelope,
filterEnvelope
},
vibratoRate: 0.5,
vibratoAmount: 0.1 // THIS IS GOOD TO MANIPULATE
});
}
let leftSynth = makeSynth();
let rightSynth = makeSynth();
let leftPanner = new Tone.Panner(-0.5).toMaster();
let rightPanner = new Tone.Panner(0.5).toMaster();
let echo = new Tone.FeedbackDelay('16n', 0.2);
let delay = Tone.context.createDelay(6.0); // Borrow the AudioContext from Tone.js
let delayFade = Tone.context.createGain();
delay.delayTime.value = 6.0;
delayFade.gain.value = 0.75;
leftSynth.connect(leftPanner);
rightSynth.connect(rightPanner);
leftPanner.connect(echo);
rightPanner.connect(echo);
// var c = canvas.getContext('2d');
echo.toMaster();
echo.connect(delay);
delay.connect(Tone.context.destination);
delay.connect(delayFade);
delayFade.connect(delay);
new Tone.Loop(time => {
leftSynth.triggerAttackRelease('C6', '1:2', time);
leftSynth.setNote('G5', '+0:2');
leftSynth.setNote('C4', '+0:4');
leftSynth.triggerAttackRelease('G5', '0:2', '+6:0');
leftSynth.triggerAttackRelease('E4', '0:2', '+11:2');
leftSynth.triggerAttackRelease('E5', '2:0', '+19:0');
leftSynth.setNote('G5', '+19:1:2');
leftSynth.setNote('A5', '+19:3:0');
leftSynth.setNote('G5', '+19:4:2');
}, '34m').start();
new Tone.Loop(time => {
// Trigger D4 after 5 measures and hold for 1 full measure + two 1/4 notes
rightSynth.triggerAttackRelease('G6', '1:2', '+5:0');
// Switch to E4 after one more measure
rightSynth.setNote('B6', '+6:0');
// Trigger B3 after 11 measures + two 1/4 notes + two 1/16 notes. Hold for one measure
rightSynth.triggerAttackRelease('D4', '1m', '+11:2:2');
// Switch to G3 after a 1/2 note more
rightSynth.setNote('G3', '+12:0:2');
// Trigger G4 after 23 measures + two 1/4 notes. Hold for a half note.
rightSynth.triggerAttackRelease('G4', '0:2', '+23:2');
}, '37m').start();
</script>
Related
This bounty has ended. Answers to this question are eligible for a +100 reputation bounty. Bounty grace period ends in 21 hours.
Basj is looking for a canonical answer:
The current answer is useful. One detail: how to have one of the layers (for example, the background layer) as an RGB image instead of just a z-axis heatmap?
I'm trying to port this answer to a 100% Plotly.JS solution.
TL;DR : how to have two heatmaps on top of eacher with an opacity slider, with Plotly.JS (no Python)?
Beginning of solution, but how to add the second trace?
const z = [];
for (let i = 0; i < 500; i++)
z.push(Array.from({ length: 600 }, () => Math.floor(Math.random() * 100)));
const data = [{ z: z, colorscale: "YlGnBu", type: "heatmap" }];
const steps = [];
for (let i = 0; i <= 100; i++)
steps.push({ label: i + "%", execute: true, method: "restyle", args: [{ opacity: i / 100 }] });
const layout = { sliders: [{ name: "slider", steps: steps, active: 100 }] };
Plotly.newPlot("graph", data, layout);
<script src="https://cdn.plot.ly/plotly-2.16.2.min.js"></script>
<div id="graph"></div>
For reference: original Python solution:
from PIL import Image
import plotly.graph_objects as go
import numpy as np
import scipy.misc
imgA = scipy.misc.face()
imgB = Image.fromarray(np.random.random(imgA.shape[:2])*255).convert('RGB')
fig = go.Figure([
go.Image(name='raccoon', z=imgA, opacity=1), # trace 0
go.Image(name='noise', z=imgB, opacity=0.5) # trace 1
])
slider = {
'active': 50,
'currentvalue': {'prefix': 'Noise: '},
'steps': [{
'value': step/100,
'label': f'{step}%',
'visible': True,
'execute': True,
'method': 'restyle',
'args': [{'opacity': step/100}, [1]] # apply to trace [1] only
} for step in range(101)]
}
fig.update_layout(sliders=[slider])
fig.show(renderer='browser')
The second trace goes into the data array as well. The thing to note is that indexing matters : the trace at index 1 is drawn above the trace at index 0, and so on.
For the slider configuration, it should be the same as in python : each step change triggers the same 'restyle' method with the same arguments, ie. Plotly.restyle(graphDiv, ...args), that is, with args such that the method call matches the signature :
Plotly.restyle(graphDiv, update [, traceIndices])
Now, the most important thing is which trace (traceIndices) the slider should target, that is, which index or which name for explicitly named traces (default is all if I'm not wrong), but again here it doesn't change between Python and Javascript.
Here is a full example (play around with it on codepen.io) :
// Random z data
const w = {length: 600};
const h = {length: 400};
const z0 = Array.from(h, () => Array.from(w, () => Math.floor(Math.random() * 100)));
const z1 = Array.from(h, () => Array.from(w, () => Math.floor(Math.random() * 100)));
// Initial opacity for the trace 'above'
const op_init = 0.5;
const data = [
// Nb. Trace 1 drawn on top of trace 0
{type: 'heatmap', z: z0, colorscale: 'Greys'}, // trace 0
{type: 'heatmap', z: z1, colorscale: 'Cividis', opacity: op_init} // trace 1
];
// Steps for the opacity slider
const steps = [];
const n_steps = 100; // number of steps above step 0
for (let i = 0; i <= n_steps; i++) {
steps.push({
label: i + '%',
execute: true,
method: 'restyle',
args: [{
opacity: i/n_steps
}, [1]] // <- Nb. this applies only to trace 1
});
}
const layout = {
width: 600,
sliders: [{
steps: steps,
active: Math.round(op_init * n_steps), // slider default matches op_init
pad: {t: 30},
currentvalue: {prefix: 'opacity: '}
}]
};
Plotly.newPlot('plot', data, layout);
Image vs Heatmap
A Heatmap works only with single channel data (individual value-to-color mappings according to a given colorscale).
When working with rgb (or rgba, rgba256, hsl, hsla), one has to use the image type. The difference is that z must be a 2-dimensional array in which each element is an array of 3 or 4 numbers representing a color (the colormodel should be set accordingly).
For example, setting an rgb image made of noise as the background layer :
const z0 = Array.from(h, () => Array.from(w, () => ['r', 'g', 'b'].map(() => Math.floor(Math.random() * 255)) ));
// ...
const data = [
{type: 'image', z: z0, colormodel: 'rgb'}, // trace 0
{type: 'heatmap', z: z1, colorscale: 'Cividis', opacity: op_init} // trace 1
];
Here a second example where we have an rgb[a] image (DOM object img) and its pixel data represented as a 1-dimensional Uint8Array (uint8Arr), which need to be converted in 2d :
const z0 = [];
const nChannels = uint8Arr.length / img.width / img.height;
const chunkSize = uint8Arr.length / img.height;
const z0_model = nChannels === 4 ? 'rgba' : 'rgb';
for (let i = 0; i < uint8Arr.length; i += chunkSize) {
const chunk = uint8Arr.slice(i, i + chunkSize);
const row = [];
for (let j = 0; j < chunk.length; j += nChannels)
row.push(chunk.slice(j, j + nChannels));
z0.push(row);
}
// ...
const data = [
{type: 'image', z: z0, colormodel: z0_model}, // trace 0
{type: 'heatmap', z: z1, colorscale: 'Cividis', opacity: op_init} // trace 1
];
Nb. When you plot an image, the yaxis is automatically reversed (unless specified otherwise, which would display the image upside down). This affects the orientation of the heatmap y-labels, as they're on the same plot, but only the labels not the data.
Here is the layout settings ensuring that both traces share the same aspect ratio and that the image is oriented correctly :
const layout = {
// ...
xaxis: {anchor: 'y', scaleanchor: 'y', constrain: 'domain'},
yaxis: {anchor: 'x', autorange: 'reversed', constrain: 'domain'},
};
I'm trying to plot an image/heatmap with a slider that will change the opacity (of the heatmap), and a second slider that will modify a custom parameter on each "onchange" event.
Once this image/heatmap is rendered, no computation should be done, and moving the sliders should be instantaneous. But from what I have tried, moving one slider is very slow (1 second lag between each position), and uses max CPU %.
I'm looking for a JS-only solution (no Python for this part).
How to make a faster slider rendering with Plotly JS?
var z = [], steps = [], i;
for (i = 0; i < 500; i++)
z.push(Array.from({length: 600}, () => Math.floor(Math.random() * 100)));
for (i = 0; i < 100; i++)
steps.push({ label: i, method: 'restyle', args: ['line.color', 'red']});
var data = [{z: z, colorscale: 'YlGnBu', type: 'heatmap'}];
var layout = {title: '', sliders: [{
pad: {t: 5},
len: 1,
x: 0,
currentvalue: {
xanchor: 'right',
prefix: 'i: ',
font: {
color: '#888',
size: 20
}
},
steps: steps
}]};
Plotly.newPlot('myDiv', data, layout);
<script src="https://cdn.plot.ly/plotly-2.16.2.min.js"></script>
<div id="myDiv"></div>
This is because you are using method: 'restyle' with the wrong args, ie. ['line.color', 'red'] the syntax is not correct and there is no line so I guess Plotly (without knowing what to restyle exactly) just redraws the whole plot whenever the slider moves, which is slow.
Basically, you can use the same slider configuration in javascript and in python for the same task (in the end the same Plotly.js slider component will be used).
For example, one can set the opacity of an image according to the slider's position, but for the changes to be applied instantly one needs to set the proper method and args in the slider' steps configuration, excactly as explained in this post :
steps.push({
label: i,
execute: true,
method: 'restyle',
args: [{opacity: i/100}]
});
Here is a full example with two sliders : one that changes the opacity of the heatmap and another one that doesn't touch the plot but only triggers a specific handler :
const z = [];
for (let i=0; i<500; i++) {
z.push(Array.from({length: 600}, () => Math.floor(Math.random() * 100)));
}
const data = [{z: z, colorscale: 'YlGnBu', type: 'heatmap'}];
// Steps for the heatmap opacity slider
const opacity_steps = [];
for (let i = 0; i <= 100; i++) {
opacity_steps.push({
label: i + '%',
execute: true,
method: 'restyle',
args: [{opacity: i/100}]
});
}
// Steps for the custom slider
const custom_steps = [];
for (let i = 50; i <= 200; i++) {
custom_steps.push({
label: i,
execute: false,
method: 'skip',
});
}
const layout = {
title: '',
sliders: [{
name: 'opacity_slider',
steps: opacity_steps,
active: 100,
pad: {t: 30},
currentvalue: {prefix: 'opacity: '}
}, {
name: 'custom_slider',
steps: custom_steps,
pad: {t: 120},
currentvalue: {prefix: 'i: '}
}]
};
Plotly.newPlot('graph', data, layout);
// Retrieve the graph div
const gd = document.getElementById('graph');
// Attach 'plotly_sliderchange' event listener to it (note that we can't specify
// which slider the handler is taking care of using a secondary selector)
gd.on('plotly_sliderchange', function(event) {
// ... so we have to distinguish between the two sliders here.
if (event.slider.name != 'custom_slider')
return;
const slider = event.slider; // the slider emitting the event (object)
const step = event.step; // the currently active step (object)
const prev = event.previousActive; // index of the previously active step
const value = step.value; // captain obvious was here
const index = step._index; // index of the current step
// ...
});
<script src="https://cdn.plot.ly/plotly-2.16.2.min.js"></script>
<div id="graph"></div>
Someone already asked that question, but they didn't seem to get the right answer.
This is my code:
const eggGenLoop = this.time.addEvent({
delay: 1500 - (gameState.score * 100),
callback: eggGen,
callbackScope: this,
loop: true,
});
so the delay doesn't change, clearly phaser just takes the initial gameState.score which is 0. two months ago i tried to make the same game, using plain JavaScript and setTimeout() / setInterval(), but i stumbled at the same obstacle, i hoped with phaser.js it would be easier.
As you mentioned, using this.time.addEvent and setting delay won't work, the reason is because the TimerEvent is created once, on
the call this.time.addEvent(...).
I see two simple options (personally I would choose option 2):
Option 1)
You could instead use setTimeout and call it recursively, and update the delay on each call.
function setTimoutWithVariableDelta( score ){
setTimeout(
_ => {
// ... do some stuff
setTimoutWithVariableDelta( gameState.score );
},
1500 - (score * 100));
}
Depending on your code, this option might not work well (and btw.: setTimeout, doesn't stop when the browser is in the background).
Option 2)
Or you could use the Scene update function.
I put the logic for this type of dynamic delay into the function callOnMuliplierInterval, it should be straight forward. Just using the time parameter of the update function to keep track of the delay. (here the link to the documentation)
Here is a possible example:
(Sorry it got abit long, I got carried away)
Just click the buttons to change the score multiplier.
document.body.style = 'margin:0;';
var config = {
type: Phaser.AUTO,
width: 536,
height: 203,
scene: { create, update },
banner: false
};
let interval = 2000;
let lastRun = -1;
let statusText;
let score = 1;
let currentDelta = 1;
let callsCount = 0;
function create () {
statusText = this.add.text(220, 20, `Score multiplier: ${score}`)
.setFontFamily('Arial')
.setFontStyle('bold')
.setFontSize('20px');
createButton(this, 40, 20, 0xff0000, '#ffffff', .33);
createButton(this, 40, 80, 0x0000ff, '#ffffff', .66);
createButton(this, 40, 140, 0x008800, '#ffffff', 1);
}
// demo helper function for creating buttons
function createButton(scene, x, y, color, textColor, multiplier){
let button = scene.add.rectangle(x, y, 140, 40, color)
.setOrigin(0);
let buttonText = scene.add.text(0, 0, `Score: ${multiplier}`)
.setFontFamily('Arial')
.setFontStyle('bold')
.setFontSize('20px');
Phaser.Display.Align.In.Center(buttonText, button);
button
.setInteractive()
.on('pointerdown', _ => score = multiplier);
}
// this function contains the important logic
function callOnMuliplierInterval(time){
let newMessage = `Score multiplier: ${score}\n`;
newMessage += `ElapsedTime: ${(time / 1000).toFixed(0)}s\n\n`;
newMessage += `CurrentDelay: ${currentDelta}\n`;
newMessage += `NextDelay: ${interval * score}\n\n`;
newMessage += `TimesCalled: ${callsCount}`;
// Execute on: first Run or when the delay has passed
if( lastRun == -1 || lastRun + currentDelta <= time ){
lastRun = time;
currentDelta = interval * score;
// HERE YOU WOULD DO THE ACTION
callsCount++;
}
// Just update the message
statusText.setText(newMessage);
}
function update(time, delta){
callOnMuliplierInterval(time);
}
new Phaser.Game(config);
<script src="https://cdn.jsdelivr.net/npm/phaser#3.55.2/dist/phaser.js"></script>
I am currently working on my graduation project in which mesh instances are re positioned over time. Besides the positions of the instances, the count of the mesh instances can also change over time.
Based on the following code examples, I managed to build this functionality.
https://jsfiddle.net/ew1tyz63/2/
https://threejs.org/examples/?q=dynami#webgl_instancing_dynamic
However, the problem appears when I want to lerp the positions of the instances. The position of all instances is resets to (0,0,0) when the count of the mesh instances changes.
I've created a codesandbox that reproduces this. The code has been forked from https://codesandbox.io/s/x8ric by James Wesc and tweaked a bit to clarify the issue.
My problem appears when you change the count of the instances by dragging the slider. The position of all instances is resets to (0,0,0).
Is there a way to stop the reset and only update the new instances when the count changes?
This is a link to the code sandbox.
https://codesandbox.io/s/instanced-mesh-lerping-positions-forked-d03ckr?file=/src/App.tsx
I added a snippet to the code as well!
Thanks in advance!!
const tempObject = new Object3D();
const tempMatrix = new Matrix4();
const tempVector = new Vector3();
const tempVector2 = new Vector3();
type XYZ = [number, number, number];
const data = dataJSON as Array<{ p1: XYZ; p2: XYZ }>;
const pos = new Vector3(10, 1, 1);
const YourCanvas = withControls(Canvas);
const Boxes: React.FC = () => {
const count = useControl("count", {
type: "number",
value: 1000,
min: 100,
max: 1000,
distance: 0.1
});
const ref = useRef<InstancedMesh>(null!);
React.useEffect(() => {
if (ref.current) {
ref.current.instanceMatrix.setUsage(THREE.DynamicDrawUsage);
}
}, []);
useFrame(({ clock: { elapsedTime } }) => {
const t = Math.floor(elapsedTime / 5) % 2;
for (let i = 0; i < count; i++) {
ref.current.getMatrixAt(i, tempMatrix);
tempVector.setFromMatrixPosition(tempMatrix);
const toPosition = t ? data[i].p1 : data[i].p2;
// Resets positions of all instances when count changes
// tempVector2.set(toPosition[0], toPosition[1], toPosition[2])
// tempObject.position.lerpVectors(tempVector, tempVector2, 0.01)
// Only updating positions of new instances when count changes
tempObject.position.set(toPosition[0], toPosition[1], toPosition[2]);
tempObject.updateMatrix();
ref.current.setMatrixAt(i, tempObject.matrix);
}
ref.current.instanceMatrix.needsUpdate = true;
});
return (
<instancedMesh
ref={ref}
args={[
new THREE.BoxGeometry(1.0, 1.0, 1.0, 1.0),
new THREE.MeshStandardMaterial({ color: new THREE.Color("#00ff00") }),
count
]}
></instancedMesh>
);
};
I am trying to subtract the value of object1's objectProperty from object2's object property and I keep getting NaN on the console. Here is example code:
object1.objectProperty - object2.object2Property
If this isn't enough to go off, I can post the full code from my project.
If there is another way to do this or some kind of function that can help, please let me know.
edit: Here is the code..
var myPokemon = {
health: 25,
defense: 5,
attack: 10,
speed: 5
};
var moves = {
Scratch: 5,
Bite: 5,
Slap: 5,
Growl: 1
};
var computerPokemon = {
health: 20,
defense: 5,
attack: 10,
speed: 7
};
function calcDamage(firstPokemon, secondPokemon, move) {
if(move == moves.Growl){
//starts here
var newDefense = moves.Growl - firstPokemon.defense;
console.log(newDefense);
//ends here
}else{
var newHealth = (firstPokemon.health + firstPokemon.defense) - (secondPokemon.attack + move);
console.log(newHealth);
}
}
edit: When I did
moves.Growl - firstPokemon.defense || 0; it returned -4 instead of NaN which is what I wanted it to do, but the person that answered that removed the answer so this has been answered by whoever that guy was.
The problem is that you are adding the object in the second argument. Also your if statement will never execute, I have fixed both as following
var myPokemon = {
health: 25,
defense: 5,
attack: 10,
speed: 5
};
var moves = {
Scratch: 5,
Bite: 5,
Slap: 5,
Growl: 1
};
var computerPokemon = {
health: 20,
defense: 5,
attack: 10,
speed: 7
};
function calcDamage(firstPokemon, secondPokemon, move) {
if(moves.Growl!=undefined){
//starts here
var newDefense = moves.Growl - firstPokemon.defense;
alert(newDefense);
//ends here
}else{
var newHealth = (Number(firstPokemon.health) + Number(firstPokemon.defense)) - (Number(secondPokemon.attack) + Number(move.Growl));
alert(newHealth);
}
}
calcDamage(myPokemon,computerPokemon,moves)
Usually, if you are getting NaN, you are probably working with other elements but numbers. Are you sure they both are numbers?
Just an example:
var x = {}, y = {};
x.r = 10;
y.r = 5;
x.r - y.r; // yields 5
Use parseInt to convert the values into integer and then do the math.
var value = parseInt(object1.objectProperty,10) - parseInt(object2.object2Property,10);
The Problem is here
var newHealth = (Number(firstPokemon.health) + Number(firstPokemon.defense)) - (Number(secondPokemon.attack) + Number(move.Growl));
The last one Number(move.Growl) it should be Number(moves.Growl) moves not move. In your case move is just a number and you are trying Number(move.Growl) which will be NaN and hence your getting NaN.