My demo.pde runs way way slower when visitng the uploaded version, why ?

edited March 2017 in JavaScript Mode

I coded a slideshow using processing3 - java - which runs fine on my PC, but when I uploaded it to my website and visited the page, the code runs stupendously slower. The htm file loads the processing.min.js file and my sketch.pde - the code in the pde is here:

int time;
int totalTime = 5000;
PImage[] img = new PImage[9];
int progress = 0;
int y1 = 0; 
int x1 = 0;
int line = 0;
int n = 0;
int wait = 400;
int delay = 2000;
int fades = 0;
int c = 0;


void setup() {
      img[0] = loadImage("b2.jpg");
      img[1] = loadImage("b4.jpg");
      img[2] = loadImage("b1.jpg");
      img[3] = loadImage("b3.jpg");
      img[4] = loadImage("b5.jpg");
      img[5] = loadImage("b6.jpg");
      img[6] = loadImage("b7.jpg");
      img[7] = loadImage("b8.jpg");
      img[8] = loadImage("black.jpg");
  size(1100, 697);
  background(0);
  frameRate(60);
}

void draw() {

  display();
  holdOn();

}


void display() {
   loadPixels();

  for(int y1 = progress; y1 < height; y1++) {
    for (int x1 = 0; x1 < width; x1++) {
      int loc1 = x1 + y1 * width;
      float r = red (img[n].pixels[x1+line]);
      float g = green(img[n].pixels[x1+line]);
      float b = blue (img[n].pixels[x1+line]);

      pixels[loc1] = color(r, g, b);

    }   }
    line = line + width; 

    updatePixels(); 

progress = progress + 1;
 while (progress < height) {
   return;}

 progress = 0;
 line = 0;

{  for (int i = 1; i < 255; i++) {  

  updatePixels(); 
  image (img[n], 0, 0);

    }  
}
  n = n + 1;
  if (n < 8){
    return; }
    else {n = 0;
    return; }
}  

void holdOn() {
  if (progress == height) {
  delay = 2;
  int time = millis();
  while(millis() - time <= delay);
} else return;}

If you visit my website link, you will see the web version http://www.stezzer.com/canvas-new.htm this is running vastly slower than the version I see on my PC when it runs from the Processing3 program where it was written. On my cell phone it is even slower.

Please can you tell me why the browser version runs way way slower than the Processing3 program ? Would really appreciate any advice or help. Thank you :)

Tagged:

Answers

  • Edit post, highlight code, press ctrl-o to format.

    Are you running the Java version locally and JavaScript version remotely? You do know the difference between compiled and interpreted languages, right?

  • edited March 2017 Answer ✓

    @Snapper4298 --

    Some feedback on cleaning this up a bit -- some of these later suggestions might make the design faster, whether in Java or JavaScript:

    1. Get rid of all those unused global variables (e.g. totalTime). Many aren't used in the code.
    2. Your code manually specifies the number of images, e.g. in loops (if (n < 8)). Make that a variable based on the array (img.length) so that if you have 1 image or 100 the code won't break.
    3. Several of your variables are really one variable, and don't all need to be declared, incremented, and reset separately -- e.g. line is really just width*progress.
    4. Lots of use of return and while makes the display and hold code hard to read -- these are really if clauses at the end of your display function.
    5. It looks like onHold() doesn't actually do anything.
    6. Currently, you load the R G B of a pixel into variables, then build a color out of them, then assign that to a new pixel. This is not necessary. Instead, copy the old pixel to the new one.
    7. If your progress is greater than your height (if scan has reached the bottom of the screen) the code does something really strange. 255 times in a row it updates the pixel buffer (although nothing has changed) and then draws the same image to the screen buffer. This all happens in a single draw frame, so the screen only refreshes once at the end after copying the same data 255 times. The funny thing is, what is being copied is already on the screen before the loop starts, because the scan (which just finished) perfectly copies the image on its last pass. Delete all of this.

    Here is a simplified example:

    PImage[] img;
    int imgNum = 0; 
    int lineCount = 0; 
    
    void setup() {
      size(512, 512);
      img = new PImage[1];
      img[0] = loadImage("https://processing.org/img/processing3-logo.png"); 
      background(0);
    }
    
    void draw() {  
      loadPixels();
      for (int y1 = lineCount; y1 < height; y1++) { 
        for (int x1 = 0; x1 < width; x1++) { 
          int loc1 = x1 + y1 * width; 
          pixels[loc1] = img[imgNum].pixels[x1+width*lineCount];
        }
      }
      updatePixels(); 
      lineCount = lineCount + 1; 
      if (lineCount >= height) {  // when done scanning, next image
        lineCount = 0; 
        imgNum++;  // next image
        if(imgNum >= img.length) imgNum = 0;  // if last image, loop to first
        background(0);  // clear screen between images
      }
    }
    

    ScanningSlideShow-screenshot

    You could try changing the approach to drawing to make it more efficient, but this at least gets back to the basics of what your demo sketch was already doing.

  • Hi Jeremy,

    Thanks for your great response. To the points that you have raised: 1. The code has been modified lots since I started working on it, trying to code a delay after each frame is rendered to hold the image on screen before the next one starts to render. This left various redundant variables as you picked up on. 2. Great advice, thanks I should have done this, but I hard coded that in at the end to get things working, your suggestion is clearly the best practice for the reasons stated, thanks for that. 3. That's newbie coding for you, again thanks for pointing that out, makes sense when I review it that way. 4. Again, newbie coding to get things working, I saw the way you coded the if statement and that's so much easier, many thanks. 5. Indeed, this was a big problem for me which led to the points you raised in 7. where I do "nothing on the screen for 255 frames" actually served as the onHold() type function I wanted. I knew it did nothing but cause a delay before the next frame renders. 6. This is new to me, when I put your code into the Processing compiler, the loadimage statement failed advising that it was issing a ")" though I could not see where it was missing! So I coded img[0] = loadImage("http://www.stezzer.com/b2.jpg"); which worked around it. 7. Again I know there is a way to hold the image by doing nothing for say, 2s, but by deleting that 255 thing altogether causes the next image to render immediately after the previous one.

    Your update has been most valuable to me. This is only the second time I have used this forum for problems with my code and you were the one who best answered my previous query so I want to thank you so much for taking the time to work through this and give me valued feedback. The code you sent I put on my website in replacement of the one I made, it does render faster though still not as fast as when the compiler shows it on my pc. Do i need to create some other kind of file on the server that will show the rendered results at the same speed I see them? I am looking into this right now. Thank you once again for your time and efforts in resolving my issue. Kindest regards, Brett.

  • edited March 2017 Answer ✓

    Very glad it was helpful.

    Delay: one way of doing a two-second delay is delay(2000) -- add it to your if statement. Be warned that this will cause your sketch to freeze and it will be unresponsive to input will delaying -- if responsiveness is important then instead set a timer and check how much time has passed.

    if (lineCount >= height) {  // when done scanning, next image
        lineCount = 0; 
        imgNum++;  // next image
        if(imgNum >= img.length) imgNum = 0;  // if last image, loop to first
        delay(2000);
        background(0);  // clear screen between images
    }
    

    Performance: as @koogs mentioned above your source Java is compiled and your exported JavaScript is interpreted in the browser, so the difference in performance when running the same design in the two different languages is not surprising.

    If you are just trying to improve the top speed of the sketch, try doing more each draw frame. For example, you could draw two lines each draw instead of one between loadPixels and updatePixels:

      loadPixels();
      for (int y1 = lineCount; y1 < height; y1++) { 
        for (int x1 = 0; x1 < width; x1++) { 
          int loc1 = x1 + y1 * width; 
          pixels[loc1] = img[imgNum].pixels[x1+width*lineCount];
        }
      }
      // render a second line this draw, then advance the new start position by two
      for (int x1 = 0; x1 < width; x1++) { 
        int loc1 = x1 + (lineCount+1) * width; 
        pixels[loc1] = img[imgNum].pixels[x1+width*(lineCount+1)];
      }
      updatePixels();
      lineCount = lineCount + 2;
    
  • Thanks again Jeremy for your informative and very useful updates. I will give this a try when I get home but can see from your suggestions that I am going to be on a winner with these updates. I can't thank you enough for your help with my processing issues, being so new to it and of course knowing I have to do a lot more reading to get to grips with it, the updates from you have given me lots of enthusiasm in this field which is still very much a blank canvas to me. You're a star, thank you once again. Brett.

  • Just to update having been able to test the code coming down on my site tonight, after the above update, getting 2 lines rather than 1 has dramatically speeded up the rendering process fixing the problem I was having where it rendered each image too slowly. Because my images are 1100 x 697 I had to make a little modification to keep the code running, if (lineCount > 694) worked perfectly. Whilst the delay(2000) works perfectly well in the compiled version on my machine, it did not seem to like this when the .pde was uploaded to the server and web page then visited. The image rendered perfectly quickly, but then when it had finished rendering, hung forever as you cautioned with using this delay instruction. I did previously try to code up a delay using the millis() and that is what ended with me having lots of redundant global variables declared after failing to get the onHold() function performing as desired. I'm going to try another approach to the delay, rather than just hold with a timed delay, I am going to try fading the completed rendered image to black and or white. Once the image has faded, then the rendering can start on the next image. Not sure how I will achieve this yet. I know the rgba pixel array contains that all important transparency byte at pixel+3 and wonder if this is going to be a solution to create a loop from 255 to 0 and alter the transparency of every rgb pixel from within that loop. I tried this earlier without success, but having a better understanding of the structure of the code to manipulate the image with the pixels array, feel like it's time to have another crack at it. I wanted to update you with my progress tonight as you have been so helpful to me breaking down my code and highlighting all the areas for improvement, and re-writing the routine in half the amount of code to show me what I was attempting to do, was actually a lot easier to do than I originally thought and for that I can't thank you enough. You have certainly helped me to progress and improve my game. I didn't hold out too much hope for posting my problem on here to get a fix, let alone a detailed one, but with your feedback, and the other pointers from koogs and gotoloop, has given me a much clearer picture of the best way to go about what I have been trying to do, and for that I can't thank you enough :)

  • Because my images are 1100 x 697 I had to make a little modification to keep the code running, if (lineCount > 694) worked perfectly.

    Ah, yes -- the sample code incorrectly assumed even-numbered image line height. There are a number of ways to fix that -- for example, copy a second line UNLESS on the last line (in which case the next line would be invalid).

      // render a second line this draw, then advance the new start position by two
      if (lineCount < height-1){
        for (int x1 = 0; x1 < width; x1++) { 
          int loc1 = x1 + (lineCount+1) * width; 
          pixels[loc1] = img[imgNum].pixels[x1+width*(lineCount+1)];
        }
      }
    

    fading the completed rendered image to black and or white

    One way: you could set blendMode(ADD) and repeatedly draw a black or white rect() while fill is set with partial transparency.

  • That blend mode add sounds like a far easier solution than the one I thought necessary dealing with pixels array. I will give this a try tonight when I get home and see what happens. Maybe a millisecond() delay between each frame too as I can imagine in processing it will perform the loop so fast it could be just photo then black or photo then white, I will have a go at this and thanks for the heads up on the blend mode add this makes perfect sense even though I've never used it yet, will update on here with the results, best regards, and thanks again, Brett

  • edited March 2017

    I've tried all night to get this blendMode(ADD) going, and I did manage to make it work using the mousePressed() function in the rendered area above the animation as the image displays to draw a rect(100,100,150,150) with fill(255,255,255,1) giving me hope that I could make it work with a while loop after the animation renders the whole image. Instead it holds the image doing nothing for the loop of 255 counts then moves onto the next image. I tried creating a function with void fadeIt(); but that did the same thing, no fade, just a hold.

    See the mousePressed function:

        void mousePressed() {
        colorMode(RGB, 255);
        blendMode(ADD);
        int i = 0;
        while (i < 255) {
        fill(255, 255, 255, 1);
        rect(0, 0, width, height);
        i += 1;
        println(i);
        }}
    

    The console confirms that i increments but the rectangle cannot be seen and so the fade effect desired does not occur. It seems that the rect() does not want to render over the top of the photo with no effect.

    This on the other hand does reveal the blending:

        void mousePressed() {
        colorMode(RGB, 255);
        blendMode(ADD);
        fill(255, 255, 255, 10);
        rect(mouseX, mouseY, 200, 200);}
    

    It feels like I'm missing something quite basic, and I only coded up the mousePressed() subroutine to see any actual results from that action. Coding up void fadeToWhite() { same code } with a loop had no effect on the image whatsoever !

    I went through the entire "Learning Processing" book of 542 pages by Daniel Shiffman and could not find any reference to the blendMode() instruction, which was even more frustrating. I googled it and followed the examples which I could evidence in my mousepressed routine, but rect(0, 0, 1100, 697) with the fill (255, 255, 255, 1) and looping the rect instruction 255 times did nothing at all.

    I really thought I was going to nail this tonight. I've had to call it version 1 and just keep trying --- feels like I'm so close, yet no cigar ^_^

  • edited March 2017 Answer ✓

    @Snapper4298 --

    It feels like I'm missing something quite basic

    I think what you may be missing is that draw() is the while loop. It is called 60 times a second. The screen only updates each time draw() returns. You can't put a while loop inside draw, or inside mousePressed, or anywhere and expect for time to pass and it to update the screen multiple times. Instead, it will update the off-screen graphics buffer many times, then the screen will get updated once when draw finally returns. Don't use while loops for delays (in general -- there are exceptions), don't try to draw progressive animation to the screen with while loops (almost ever -- it is usually impossible). draw() is the loop for drawing to the screen; use draw().

    Here is an example of using a simple int timer to decide what the draw loop will do. When the timer expires each mode switches to the other mode.

    String mode;
    int startTime;
    
    void setup(){
      mode = "drawPhoto";
      blendMode(ADD);
      textAlign(CENTER,CENTER);
      background(0);
    }
    
    void draw(){
      if(mode=="drawPhoto"){ drawPhoto(); }
      if(mode=="fade"){ fade(); }
    }
    
    void drawPhoto(){
      background(0);
      fill(255);
      text(millis()-startTime, width/2, height/2);
      if(millis()>startTime+4000){
        mode="fade";
        startTime=millis();
      }
    }
    
    void fade(){
      fill(255,3);
      rect(0,0,width,height);
      if(millis()>startTime+2000){
        mode="drawPhoto";
        startTime=millis();
        background(0);
      }
    }
    

    In this example blendMode(ADD) with a partially transparent fill works exactly as you imagined -- but you can actually see it working, because it runs over a series of different draw() calls (different screen updates) rather than all at once in a single frame.

  • It makes sense now, a bit of a schoolboy error on my part there, I remember reading that draw continually loops thinking whilst I'm still in the draw loop it will carry on doing as told until the return. My error was telling it to draw multiple frames of animation before the next pass. Looking at your code also makes sense where it checks the status of each process, IE render or fade in my case, then send the draw to those routines until they direct the loop to the next job and so on. I can see now why my code just hung then carried on, being no draw update stuck in that loop. I've learnt a lot these last few days. I'm sure I will encounter many more issues as you do, but understanding these principles where you have pointed them out has been invaluable to me and has inspired me to do more, when last night I felt like giving up haha. Thank you so much Jeremy, for all your continued support.

  • Here's my updated code, which thanks to all the superb feedback from Jeremy, after plenty of trial and error on my part now works exactly how I wanted it to. I am going to add different fading effects in the days ahead, now that I have a structure that is fit for purpose and a much clearer understanding of how this language works through it's instruction set:

    String mode;
    int startTime;
    int lineCount;
    PImage[] img;
    int imgNum = 0; 
    
    void setup() {
      size(1100, 697);
      mode = "drawPhoto";
      blendMode(SUBTRACT);
      img = new PImage[4];
      img[0] = loadImage("http://www.stezzer.com/b2.jpg");
      img[1] = loadImage("http://www.stezzer.com/b3.jpg");
      img[2] = loadImage("http://www.stezzer.com/b6.jpg");
      img[3] = loadImage("http://www.stezzer.com/b7.jpg");
    }
    
    void draw() {
      if(mode=="drawPhoto"){ drawPhoto(); }
      if(mode=="fade"){ fade(); }
       if(mode=="hold"){ hold(); }
    }
    
    void drawPhoto() {
      loadPixels();
    for (int y1 = lineCount; y1 < height; y1++) { 
      for (int x1 = 0; x1 < width; x1++) { 
        int loc1 = x1 + y1 * width; 
        pixels[loc1] = img[imgNum].pixels[x1+width*lineCount];
      }
    }
    // render a second line this draw, then advance the new start position by two
    if (lineCount < height-1){
      for (int x1 = 0; x1 < width; x1++) { 
        int loc1 = x1 + (lineCount+1) * width; 
        pixels[loc1] = img[imgNum].pixels[x1+width*(lineCount+1)];
      }
    }
    updatePixels();
    
    lineCount = lineCount + 2;      // as we rendered 2 lines in the draw pass
    
     if (lineCount > (height-1)) {  // when rendering completes
        lineCount = 0;              // reset rendering pointer
        imgNum++;                   // select next image
        if(imgNum >= img.length) imgNum = 0;  // if last image, loop to first
        mode="hold";                //hold rendered image on screen
        startTime=millis();
      }
    }
    
    void fade() {
      fill(255,3);
      rect(0,0,width,height);
      if(millis()>startTime+3000){ // fade image from screen over 3s
        mode="drawPhoto";          // then start to draw the next photo
        startTime=millis();
      }
    }
    
    void hold() {
      if(millis()>startTime+3000){  // hold image on screen for 3s
        mode="fade";                // then start to fade it out
        startTime=millis();
      }
    }
    

    I can't thank you enough for your help and support Jeremy, you have really given me lots of enthusiasm and inspiration. Hats off to you my friend ^_^

  • Answer ✓

    Minor optimization in draw will save evaluation of mode 3 times every draw:

    void draw() {
      if (mode=="drawPhoto") { 
        drawPhoto();
      }
       else if (mode=="fade") { 
        fade();
      }
      else if (mode=="hold") { 
        hold();
      }
    }
    
  • edited March 2017

    One other optimization would be to make mode to an int, then the mode == evaluations would be int compares instead of String compares.

  • Thanks for these updates dude ^^, I got the thing going and I'm timing sounds with the slides with varying success, using minim library, will be exploring other options in the days ahead ~^

  • edited March 2017 Answer ✓

    Just a word of caution: String in Java should use its equals() method, not the equality == operator: L-)
    https://Processing.org/reference/String_equals_.html

    For example, instead of if (mode == "drawPhoto") {},
    It's safer this way: if ("drawPhoto".equals(mode)) {}

    Of course, since everything is only compared & assigned to variables from String literals, the equality == operator miraculously work, b/c Java collects & caches them all. ~O)

    However be careful: Taking such risks can bite you hard back later! ^#(^

  • Thanks for the tweaks dude, i will implement this evening :)

Sign In or Register to comment.