Current progress:
Some time ago I began doing some perf tests of the HTML5 2D Canvas to see if it was a viable option for making a performant graphics application. I wouldn't be worried if I were making a traditional pixel art application where you only need to directly draw pixels to the screen, but my requirements were far greater. In addition to direct pixel blitting, I needed to handle not just soft brushes and blend modes, but also redrawing the entire canvas twice for any canvas mutation; once for the change itself (brush stroke or whatever), and again to re-index it into pixel art which can be quite expensive.
I made a test program that generates a soft brush with a gradient and simulates a low-opacity brush stroke moving around the canvas which builds up over time. I optimized the indexed canvas by only recalculating any "dirty" part of the canvas that has changed.
Indexed blit test in Chrome (left) and firefox (right) with HD data on top, and their respective indexed version below

live demo:
http://danfessler.com/projects/blit/I was pretty satisfied with the results. With some optimization, the performance was acceptable with limited brush sizes, however, it was apparent it would be very annoying to keep the two canvases synchronized. Unfortunately, there were some inconsistent artifacts in different browsers. For some reason, chrome was dithering my canvas! Although that definitely is an intended feature down the road, I want it to be user controlled. The issue turned out to be with the generated gradient the brush was using. Chrome dithers gradients to give a smoother appearance and there is no way of turning it off. I couldn't use static images for brushes because they need to be dynamic (size, softness, angle, etc). The only way around this is to generate the brush pixel-by-pixel, which would be yet another convoluted step to maintain and performance hit. This was getting close to a deal breaker.
in parallel, I was also developing a UI-framework, written in React, that I could leverage for various projects and tools that was largely inspired by Adobe. I even open sourced a small piece of it called React-PanelGroup (
https://github.com/DanFessler/react-panelgroup) which has been fairly successful, seeing over 1000 downloads per week and has even been picked up by Facebook.
Screen Shot of React-based UI framework

I decided to combine my learnings from these two experiments and begin writing the app - this time using WebGL for canvas drawing. I'd never used WebGL before, but I've gotten fairly familiar with 3d graphics pipelines at work with Unity and Unreal so the transition wasn't too bad. WebGL is definitely FAR more verbose. Something that used to take 10 lines can easily take over 100. But in lieu of being terse, it makes up for it in raw power and control. No longer am I subject to the whims of each browser implementation, I'm talking directly to the graphics card and can control every pixel exactly how I desire.
I reimplemented photoshop's basic brush settings (size, flow, hardness, spacing, roundness, and angle) with the exception of opacity as that would require a two-buffer setup. Immediately it was clear that WebGL was the answer. The performance was insanely good. It felt desktop-class.
First WebGL painting test with photoshop-style brush settings including pen pressure:

Despite the performance boost, If the mouse moved too quick there wouldn't be enough data to draw a smooth brush stroke and would end up with a hard-angled line, so I used a Hermite spline to interpolate between mouse samples. This required that the brush stroke would unavoidably lag behind the brush by 1-frame, but it was a worthy tradeoff. It was particularly tricky to maintain the "spacing" setting which controls the pixel distance between each stamp of the brush as there's no way to walk a Hermite spline a specified unit length. With the advice from some fine folks at a local Hype O'Clock game-dev meetup, the solution was to break the spline into a series of linear segments, and then walk them traditionally.
Brush stroke before and after Hermite interpolation:

This past couple weeks has been spent polishing the UI, including tab switching, styling, text-inputs etc. I also finished the widget context menu which supports actions, radio-select, and multi-select options
