About a month ago, I posted on Reddit some information about my CamanJS image manipulation library and asked people to submit their benchmark times. I was mainly curious to see how much render time differed from browser to browser.
After spending some time away from CamanJS for a bit, I decided to revisit it tonight and do some hacking. Primarily I was interested in improving its speed, since I felt that was the main area that was lacking.
Previously, CamanJS did all of its computations in web workers. The thought behind it was: by using web workers, the browser would be able to run the computations without blocking the page, and in a separate thread. Makes sense right? Well… while the page was able to render somewhat quickly, the images took way too long to finish rendering.
Tonight I rewrote (mostly by means of selective copy & paste) CamanJS to work without web workers. I was curious what the speed change would be at the expense of blocking the page from rendering until the image manipulation had finished.
It turns out, the difference is massive. Some filters experienced a 3500% speed increase by not using web workers. In the end, unless you have a very large number of images to manipulate on the same page, blocking the page from rendering is definitely worth the speed increase because the page won’t be blocked for very long.
Of course, both methods still have their pros and cons:
With Web Workers
- Pros
- Doesn’t block page rendering.
- Keeps all computations in a separate thread, so other Javascript in the main window isn’t held up either.
- Doesn’t require a call to render()
- Cons
- Ridiculously slow. Can cause confusion to the viewer sometimes because it takes so long to render.
- Significantly more complex code.
Without Web Workers
- Pros
- Super fast.
- Code is much easier to follow and is less complex.
- Only updates the canvas when rendering is complete, which is part of why its so much faster.
- Cons
- Blocks page from rendering until the images are finished being modified.
- Can hold up other Javascript code until its done rendering.
A Small API Change
In order to get the maximum amount of speed out of CamanJS, it needs to know when to write the updated pixel data to the canvas. By doing this once, instead of after each filter finishes, render time is improved significantly. It turns out this is actually the slowest part of the entire process. So, to accommodate this change, there is a new function call that is required in order to finish the rendering process. It’s aptly named render() and must be called after all of the filters are called (order matters!):
Caman('path/to/image.jpg', '#caman-image', function () {
this.gamma(1.2);
this.brightness(5);
this.contrast(5);
// New render() function. Callback is optional.
this.render(function () {
this.save('png');
});
});
Show me your Times!
I would love to see the render times you are getting now that CamanJS no longer uses web workers. Take a gander over to the benchmark page and post your results here in the comments, along with your browser type and version. Just a warning: Firefox 3 is still slow, but it’s faster than it was before.















Hi,
Nice work with CamanJS! I have been looking for a lib like this. It seems to have a lot of basic stuff implemented I’m going to need later on. Just need to add blur and some flips myself.
Have you tried rendering using intervals? This should make the lib non-blocking. There’s a small performance penalty, though. No idea if that’s too significant…
My benchmark results.
Yeah, I was thinking about looking into using setInterval, but I have a feeling that the computation is so heavy that the page will probably render in the same amount of time that it does now.
Thanks for checking it out! If you need any help modifying it, let me know!
I think it still may worth a shot. You could try chunking up the computation. Split the image to 4×4 blocks or so and then process each per interval.
Have you decided which license to use? I didn’t see any mention of this in the project docs.
I took a quick look at your code. Some comments:
It might be a good idea to implement some sort of iterator for kernel based algos (convolution, blur, whatnot). That would be cool!
I’m still a bit unsure what’s the best way to handle flips… The math is trivial. Perhaps there just need to be special iterators for those (one gives access to row pixels while other to column ones).
It may be a good idea to separate those color related parts to a library of its own. I use this in my own development. It’s probably missing some bits you need. It shouldn’t be too hard to add those there if you want to.
In case you want to split up the source a bit take a look at RequireJS. With some effort it will give you nice means to provide optimized builds even. It uses Google’s Closure Compiler for this particular purpose.
I’m not sure if I’m going to be able to hack your lib anytime soon. I will keep it in mind, though! If I mod it, you’ll be first to know.
Thanks for the lib.
Pingback: JavaScript Magazine Blog for JSMag » Blog Archive » News roundup: Chrome ditches H.264, Mobile Perf bookmarklet, Reddit’s list of JavaScript game engines
Good job on the benchmark. I’m glad it works in Opera now.
Benchmark Results:
http://pastebin.com/download.php?i=NEaSmJFV
Used ~ most recent developmental version of each browser.
Pingback: CamanJS Revisited – Improving Render Times by Ditching Web Workers | Farkas Máté
Hi,
Are you likely to have an option to use webworkers or is it ditched completely? I get the point of speed increase if only manipulating a few images, but I was hoping to do it with many for my usage in which case I would be happy for the manipulation to be slower rather than page load slower.
This worries me “unless you have a very large number of images to manipulate on the same page, blocking the page from rendering is definitely worth the speed increase”
If you could choose to turn webworkers on (with them off as default it would be great)
This post is actually a little outdated now. While CamanJS still does not use Web Workers, it no longer blocks page rendering because it runs all rendering asynchronously.
While I would still be interested in experimenting with Web Workers in the future, there are no immediate plans to re-implement them. If you are modifying many small images, the whole process should be relatively quick.