I'm trying to create an application where circles are drawn onto the canvas through reading information from a Firebase database that stores the x and y coordinates of the circles. Executing the code below however, simply produces nothing, without any sign of the circles, because the function drawCricles runs asynchronously, and thus the command background(40)
clears everything before the circles can be drawn.
Here is my code:
function setup() {
createCanvas(windowWidth, windowHeight);
background(40);
stroke(80);
smooth();
frameRate(60);
}
function drawCircles() {
firebase.database().ref("circles").once("value", function(snapshot) {
var snapshotVal = snapshot.val();
var circleCount = snapshotVal.numCircles;
for (var j = 0; j < circleCount; j++) {
firebase.database().ref("circles" + j).once("value", function(snapshot) {
var snapshotValue = snapshot.val();
fill(143, 2, 2);
ellipse(snapshotValue.xPos, 50, 50);
});
}
});
}
function draw() {
stroke(80);
background(40);
stroke(0);
drawCircles();
}
It seems your problem is simply 60 frames per second, which is causing a foot-race condition. Firebase .once
will execute async when it completes fetching, and P5 won't wait for it to fetch because it'll stick with its framerate timing.
In this specific case I have multiple recommendations which will hopefully get you very close to the result you desire.
1 - Restructure your code
There's two issues with the current structure of your code.
Case 1 : Your current code would lead me to think your circles are updated in realtime in the database, and you need to stay up to date, so you keep fetching their latest position. If this is the case, you should use .on("value")
instead of .once("value")
and let firebase send you the updates whenever circles change, instead of asking it 60 times a second to save roundtrip request time. If this is the case : See my solution 1 below.
Case 2 : If your circles aren't updated real-time in the database, and you just want the whole list of circles, then you're fetching the list 60 times a second for no reason. You should instead fetch the list using .once
upon setup and iterate through that list in draw()
later. See solution 2 below.
2 - Restructure your database
In either case, your current database model requires you to keep fetching in a loop. Which means you're making as many requests as your circleCount
. This is bad for your usage, simply because each request takes additional trip time, and we're trying to reduce the time it takes, so that it would be closer to real-time. (or match the framerate)
Currently your circles are seemingly saved as circles1
circles2
etc all at root, because you're using .ref("circles" + j)
to retrieve them. Make it so that you save your circles like this : .ref("circles/" + j)
that would mean that each circle
is now saved in circles
. like circles/circle1
circles/circle2
etc.
The benefit of this is that now you don't need the additional requests to firebase get all circles. Firebase has incredibly convenient things like forEach to iterate through all children with a single request.
3 - Clear background in your firebase callback
Currently, you clear the background in a frame-rate specific manner. This means that if each of your firebase calls take longer than 1/60th of a second (16 miliseconds) you will have cleared the background and move on. Chances of you achieving this speed is very low even after we structure our database. so instead, I would recommend first using 30fps, which would also reduce the number of calls you will make to firebase to 30 calls per second.
Solution 1
If your circles are updated in the database (say for example by some other game-player or someone else, and you want your code to always display the latest xPos)
var latestCirclePositionsSnapshot;
function setup() {
createCanvas(windowWidth, windowHeight);
background(40);
stroke(80);
smooth();
frameRate(60);
firebase.database().ref("circles").on("value", function(snapshot) {
// got a new value from database, so let's save this in a global variable.
latestCirclePositionsSnapshot = snapshot;
// we will keep drawing this update until we get a new one from the database.
});
}
function draw() {
drawCircles();
}
function clearBackground () {
stroke(80);
background(40);
}
function drawCircles() {
clearBackground();
stroke(0);
latestCirclePositionsSnapshot.forEach(function(circleSnapshot) {
// circleData will be the actual contents of each circle
var circleData = circleSnapshot.val();
fill(143, 2, 2);
ellipse(circleData.xPos, 50, 50);
});
}
Basically this will keep drawing the last circle positions we got from firebase until we get a new one. (so P5 will keep refreshing at 60fps, but your firebase updates will be as realtime as firebase can run and fetch from firebase etc.)
Solution 2
If you don't have real-time updates in your database, and all you'd like is to draw circles by getting data from firebase once (say for example to plot some dots based on some data)
var circlePositions;
var gotPositions = false;
function setup() {
createCanvas(windowWidth, windowHeight);
background(40);
stroke(80);
smooth();
frameRate(60);
firebase.database().ref("circles").once("value", function(snapshot) {
// got the circle values from the database
// let's store them and we'll keep drawing them forever.
circlePositions = snapshot;
gotPositions = true;
});
}
function draw() {
drawCircles();
}
function clearBackground () {
stroke(80);
background(40);
}
function drawCircles() {
clearBackground();
stroke(0);
if (gotPositions) {
circlePositions.forEach(function(circleSnapshot) {
// circleData will be the actual contents of each circle
var circleData = circleSnapshot.val();
fill(143, 2, 2);
ellipse(circleData.xPos, 50, 50);
});
} else {
// Display some text here like "LOADING DATA FROM SERVERS..."
}
}
Hope these help :) It's good to see another fellow fan of Processing & Firebase.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With