Why are custom fonts slowing down my program? - javascript

So I’m making a project in p5.js which aim to reproduce the Matrix rain code effect.
It ran pretty smooth until I decided to load a custom font.
When I did, my framerate dropped considerably. And it’s the case for every custom font that I try to load (only when I use the loadFont function; if I use textFont and some default font it doesn’t seem to slow down)
Of course, I use the loadFont function in the preload function, and textFont in setup, so I don’t know if this is a known thing ? How could I avoid this ?
Here is a Minimum Viable Example of the problem :
var characters = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789$+-*/÷=%\"'#&_(),.;:?!\\|{}<>[]^~ "
var font;
var spacing_width = 10;
var spacing_height = 20;
function preload() {
font = loadFont('matrix_font.otf');
}
function setup() {
createCanvas(window.innerWidth, window.innerHeight);
frameRate(60);
background(0);
textFont(font);
fill(255)
}
var lastUpdate;
function draw()
{
background(0);
var timeSinceLastUpdate = new Date().getTime() - lastUpdate;
print(timeSinceLastUpdate + "ms") // prints time elapsed between each frame
lastUpdate = new Date().getTime();
for(var i = 0; i < 40; i++)
for(var j = 0; j < 40; j++)
text(characters[int(random(0, characters.length))], j * spacing_width, i * spacing_height)
}
This code prints in the console values around 40-50ms, but when I comment out the line textFont(font); the values are around 10-20ms
I'm using this font but I have the problem with every font that I try to load

See https://github.com/processing/p5.js/issues/3435 for a discussion of this issue

Related

Make p5js project run faster

I made a project called "pixel paint" by javascript with p5js library, but when I run it, that project ran too slow. I don't know why and how to make it run faster. And here is my code:
let h = 40, w = 64;
let checkbox;
let scl = 10;
let painting = new Array(h);
let brush = [0, 0, 0];
for(let i = 0; i < h; i++) {
painting[i] = new Array(w);
for(let j = 0; j < w; j++) {
painting[i][j] = [255, 255, 255];
}
}
function setup() {
createCanvas(w * scl, h * scl);
checkbox = createCheckbox('Show gird line', true);
checkbox.changed(onChange);
}
function draw() {
background(220);
for(let y = 0; y < h; y++) {
for(let x = 0; x < w; x++) {
fill(painting[y][x]);
rect(x * scl, y * scl, scl, scl);
}
}
if(mouseIsPressed) {
paint();
}
}
function onChange() {
if (checkbox.checked()) {
stroke(0);
} else {
noStroke();
}
}
function paint() {
if(mouseX < w * scl && mouseY < h * scl) {
let x = floor(mouseX / scl);
let y = floor(mouseY / scl);
painting[y][x] = brush;
}
}
<!--Include-->
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.4.0/p5.min.js"></script>
Is there a solution to make my project run faster?
The code you have is easy to read.
It might not be worth optimising at this stage as it would make the code potentially needlessly more complex/harder to read and change in the future.
If you want to learn about different ways you could achieve the same thing I can provide a few ideas, though, for your particular use case int terms of performance might won't make a huge difference:
Instead of using the painting as a nested [w][h] array you could use a flat [w * h] array and use a single for loop instead of a nested for loop. This would be somewhat similar to using pixels[]. (You can convert x,y to an index (index = x + (y * width)) and the other way around(x = index % width, y = floor(index / width))
You could in theory use a p5.Image, access pixels[] to draw into it and render using image() (ideally you'd get lower level access to the WebGL renderer to enable antialiasing if it's supported by the browser). The grid itself could be a texture() for a quad where you'd use vertex() to specify not only x,y geometry positions, but also u, v texture coordinates in tandem with textureWrap(REPEAT). (I posted an older repeat Processing example: the logic is the same and syntax is almost identical)
Similar to the p5.Image idea, you can cache the drawing using createGraphics(): e.g. only update the p5.Graphics instance when the mouse is dragged, otherwise render the cached drawing. Additionally you can make use of noLoop()/loop() to control when p5's canvas gets updated (e.g. loop() on mousePressed(), updated graphics on mouseMoved(), noLoop() on mouseReleased())
There are probably other methods too.
While it's good to be aware of techniques to optimise your code,
I strongly recommend not optimising until you need to; and when you do
use a profiler (DevTools has that) to focus only the bits are the slowest
and not waste time and code readability on part of code where optimisation
wouldn't really make an impact.

How to clear part of the buffered image with p5.js

I am using javascript with p5.js framework. I created an offscreen graphics buffer. Now I want to clear part of that buffer (so it becomes invisible again). What is the best way to do it?
Right now I can only achieve that by direct changing of the alpha value for every pixel I need.
Here is a MCVE:
// sketch.js, requires p5.js
function setup() {
createCanvas(100,100);
background(0);
let mymap = createGraphics(100,100);
mymap.fill(255);
mymap.rect(20,20,40,40);
mymap.loadPixels();
for (let i = 23; i < 37; i++) {
for (let j = 23; j < 37; j++) {
mymap.pixels[400*i+4*j+3] = 0;
}
}
mymap.updatePixels();
image(mymap,0,0);
}
Some side-notes:
1) there is a tiledmap.js library to p5.js, but I am still reading into it's source code and right now it doesn't look like they got 1 buffer for the entire tilemap.
2) there is a clear function to clear the canvas graphics, it does clear things, but it clears everything on the given buffer.
You should be able to use the set() function. More info can be found in the reference.
I would expect something like this to work:
const transparency = createGraphics(20, 20);
mymap.set(30, 30, transparency);
mymap.updatePixels();
Unfortunately, this seems to only set a single pixel. This smells like a bug to me, so I've filed this bug on GitHub.
Like you've discovered, you can also set the pixel values directly. This might be simpler than what you currently have:
let mymap;
function setup() {
createCanvas(100,100);
mymap = createGraphics(100,100);
mymap.fill(255, 0, 0);
mymap.rect(20,20,40,40);
for(let y = 30; y < 50; y++){
for(let x = 30; x < 50; x++){
mymap.set(x, y, color(0, 0, 0, 0));
}
}
mymap.updatePixels();
}
function draw() {
background(0, 255, 0);
image(mymap,0,0);
}
Note that there's a tradeoff: this code is simpler, but it's also slower.

Pixi.js draw falling squares

I drawed a grid based system on canvas using PIXI.js.
I'm trying to animate the thing, first each particle position.y is -200, then using Tween.js I'm trying to make them fall.
I change the position to the correct position, which is particle._y.
As you notice you will see after falling there are some empty spaces and CPU is over heating.
http://jsbin.com/wojosopibe/1/edit?html,js,output
function animateParticles() {
for (var k = 0; k < STAGE.children.length; k++) {
var square = STAGE.children[k];
new Tween(square, 'position.y', square._y, Math.floor(Math.random() * 80), true);
}
}
I think I'm doing something wrong.
Can someone please explain me what I'm doing wrong and why there are some empty spaces after falling?
The reason for the empty spaces is that some of your animations are not starting. The cause is in this line:
new Tween(square, 'position.y', square._y, Math.floor(Math.random() * 80), true);
Looking at your function definition for Tween.js, I see this:
function Tween(object, property, value, frames, autostart)
The fourth parameter is frames. I'm assuming this is the number of frames required to complete the animation.
Well your Math.floor function willl sometimes return zero, meaning the animation will have no frames and won't start!!
You can fix this by using math.ceil() instead. This way there will always be at least 1 frame for the animation:
new Tween(square, 'position.y', square._y, Math.ceil(Math.random() * 80), true);
Now, as for performance, I would suggest setting this up differently...
Animating all those graphics objects is very intensive. My suggestion would be to draw a single red square, and then use a RenderTexture to generate a bitmap from the square. Then you can add Sprites to the stage, which perform WAY better when animating.
//Cretae a single graphics object
var g = new PIXI.Graphics();
g.beginFill(0xFF0000).drawRect(0, 0, 2, 2).endFill();
//Render the graphics into a Texture
var renderTexture = new PIXI.RenderTexture(RENDERER, RENDERER.width, RENDERER.height);
renderTexture.render(g);
for (var i = 0; i < CONFIG.rows; i++) {
for (var j = 0; j < CONFIG.cols; j++) {
var x = j * 4;
var y = i * 4;
//Add Sprites to the stage instead of Graphics
var PARTICLE = new PIXI.Sprite(renderTexture);
PARTICLE.x = x;
PARTICLE.y = -200;
PARTICLE._y = H - y;
STAGE.addChild(PARTICLE);
}
}
This link will have some more examples of a RenderTexture:
http://pixijs.github.io/examples/index.html?s=demos&f=render-texture-demo.js&title=RenderTexture

Very laggy animation in javascript

I have made a board with 156X64 divs 3 pixel each with border radius, so it looks like a board out of LED. I have string representing 0 or 1 of each 7X5 matrix of letters.
var lgeoa="00100001000001000001100011000101110";//7X5 matrix letter A
var lgeob="10000111000010001010100011000101110";//7X5 matrix letter B
and so on...
Drawing letter means change corresponding div background color. It is working fine, but after that I wanted to animate them the problem started. I clear line and draw in every 10 milliseconds, but its very very laggy. So please how can this code be optimized to work without lags?
P.S. Surprisingly it's working better in IE11 rather than in chrome.
Here is a fiddle
There's a lot of optimization that can be done here. I'll point out a couple.
Starting with the animate function, the first thing I notice is that you're running a bit of code every 10ms. Why don't we check out what's being run?
function animate() {
var string = "აბგდევზთიკლმნოპჟრსტუფქღყშჩცძწჭხჯჰ ტესტ ტესტ აი ემ ე თეიბლ ტექსტი იწერება აქ"; //string to animate
position = 150; //initial position of string
window.setInterval(function () {
clearLine(0);
drawOnBoard(string, position, 0);
position = position - 1;
}, 10);
}
Clearline is the first function.
function clearLine(n){
for(var i=n*symbolHeight*lineWidth+n*lineWidth;i<(n+1)*symbolHeight*lineWidth+n*lineWidth;i++)
leds[i].style.backgroundColor="black";
}
That's a bit of a mess in the for loop. My understanding is that non-compiled code will run all of that math for every single iteration. So let's move it out of the for loop.
function clearLine(n) {
var initial = n * symbolHeight * lineWidth + n * lineWidth;
var length = (n + 1) * symbolHeight * lineWidth + n * lineWidth;
for (var i = initial; i < length; i++)
leds[i].style.backgroundColor = "black";
}
Ah but there's still more to be done. I see that both equations have a lot of shared math.
function clearLine(n) {
var whateverThisIs = symbolHeight * lineWidth + n * lineWidth;
var initial = n * whateverThisIs;
var length = (n + 1) * whateverThisIs;
for (var i = initial; i < length; i++)
leds[i].style.backgroundColor = "black";
}
I saw that you're moving on so I'll stop working on this for now. There's still plenty more to optimize.
Here's a fiddle of the updated version.

HTML 5 canvas animation - objects blinking

I am learning ways of manipulating HTML 5 Canvas, and decided to write a simple game, scroller arcade, for better comprehension. It is still at very beginning of development, and rendering a background (a moving star field), I encountered little, yet annoying issue - some of the stars are blinking, while moving. Here's the code I used:
var c = document.getElementById('canv');
var width = c.width;
var height = c.height;
var ctx = c.getContext('2d');//context
var bgObjx = new Array;
var bgObjy = new Array;
var bgspeed = new Array;
function init(){
for (var i = 1; i < 50; i++){
bgObjx.push(Math.floor(Math.random()*height));
bgObjy.push(Math.floor(Math.random()*width));
bgspeed.push(Math.floor(Math.random()*4)+1);
}
setInterval('draw_bg();',50);
}
function draw_bg(){
var distance; //distace to star is displayed by color
ctx.fillStyle = "rgb(0,0,0)";
ctx.fillRect(0,0,width,height);
for (var i = 0; i < bgObjx.length; i++){
distance = Math.random() * 240;
if (distance < 100) distance = 100;//Don't let it be too dark
ctx.fillStyle = "rgb("+distance+","+distance+","+distance+")";
ctx.fillRect(bgObjx[i], bgObjy[i],1,1);
bgObjx[i] -=bgspeed[i];
if (bgObjx[i] < 0){//if star has passed the border of screen, redraw it as new
bgObjx[i] += width;
bgObjy[i] = Math.floor(Math.random() * height);
bgspeed[i] = Math.floor (Math.random() * 4) + 1;
}
}
}
As you can see, there are 3 arrays, one for stars (objects) x coordinate, one for y, and one for speed variable. Color of a star changes every frame, to make it flicker. I suspected that color change is the issue, and binded object's color to speed:
for (var i = 0; i < bgObjx.length; i++){
distance = bgspeed[i]*30;
Actually, that solved the issue, but I still don't get how. Would any graphics rendering guru bother to explain this, please?
Thank you in advance.
P.S. Just in case: yes, I've drawn some solutions from existing Canvas game, including the color bind to speed. I just want to figure out the reason behind it.
In this case, the 'Blinking' of the stars is caused by a logic error in determining the stars' distance (color) value.
distance = Math.random() * 240; // This is not guaranteed to return an integer
distance = (Math.random() * 240)>>0; // This rounds down the result to nearest integer
Double buffering is usually unnecessary for canvas, as browsers will not display the drawn canvas until the drawing functions have all been completed.
Used to see a similar effect when programming direct2d games. Found a double-buffer would fix the flickering.
Not sure how you would accomplish a double(or triple?)-buffer with the canvas tag, but thats the first thing I would look into.

Categories