News for hackers. Stuff that matters.
Re:It's always been you, Canvas2D by simonsarris on Friday March 04, 2022 @08:43AM (#30554600)
I have been building a Canvas diagramming library since 2010 (https://gojs.net), if anyone has any questions about 2D Canvas use in the real-world I'd be happy to answer them.

roundRect is great. Though you don't need 4 arcTo in order to make a rounded rect, you can use bezier instead (we do). Their example is also 1% amusing because they set the `fillStyle` but then call `stroke` (and not `fill`). I'll have to do some performance comparisons, since that's the operative thing for my use case (and any library author).

text modifiers are very welcome. It's crazy how annoying measuring still is, especially if you want thinks to look perfectly consistent across browsers. Though the chrome dominance is making things easier in one way, I guess.

context.reset is kinda funny. Most high-performance canvas apps will never want to use it. For that matter you want to set all properties as little as possible, especially setting things like context.font, which are slow even if you're setting it to the same value. (Or it was, I haven't tested that in several years).

I'm sure most users know this by now, but generally for performance the fewer calls you make to the canvas and the context, the beter. This is even true of transforms: It's faster to make your own Matrix class, do all your own matrix translation, rotation, multiplication, etc, and then make a single call to `context.setTransform`, than it is to call the other context methods.

One of the greatest things to happen to 2D canvas in all these years was hardware acceleration. It used to be significantly slower. (You could switch to WebGL canvas, but at a cost of complexity and all kinds of annoyances rendering text, and if you had a lot of text it often wasn't worth it.) Thinking back, I'm also a little disappointed that after the death of Flash, it felt like Canvas never really took its place. The playful web that came before it in some ways simply closed up.

Re:It's always been you, Canvas2D by mysteryDate on Friday March 04, 2022 @09:27AM (#30555205)
This is all great feedback!

  I'm sure most users know this by now, but generally for performance the fewer calls you make to the canvas and the context, the beter.
This is almost entirely due to the javascript engine. We're working on ways to improve it.

  roundRect is great. Though you don't need 4 arcTo in order to make a rounded rect, you can use bezier instead (we do).
Yeah, the example was a little bit humorous, I kinda brute forced it.

  Their example is also 1% amusing because they set the `fillStyle` but then call `stroke` (and not `fill`).
You caught what 100 rounds of edits did not.
Re:It's always been you, Canvas2D by moonchrome on Friday March 04, 2022 @11:35AM (#30556982)
>This is almost entirely due to the javascript engine. We're working on ways to improve it.

Is it a logo interpreter built into the rendering engine ?

    repeat 2 [fd 100 repeat 90 [rt 1 fd 0.3] fd 200 repeat 90 [rt 1 fd 0.3]]
A rounded rect !
Re:It's always been you, Canvas2D by danielvaughn on Friday March 04, 2022 @02:52PM (#30559522)
Sounds like you're involved in this effort, so I'm taking the opportunity to ask...what about the TextMetrics API? It's listed as supported by Chrome, but I remember trying it last year and it's definitely not supported. Has there been progress on that front?
Re:It's always been you, Canvas2D by andai on Friday March 04, 2022 @11:48AM (#30557152)
>Thinking back, I'm also a little disappointed that after the death of Flash, it felt like Canvas never really took its place. The playful web that came before it in some ways simply closed up.

I've been wondering if this is cultural or technical. The things that are easy to do get done more, and thus the tech (and how well it's designed) shapes mass behavior and culture. On the other hand, where there's a will to do something, a way will be found, or created...

A great example is sending Flash holiday greeting cards in the email. Watching them with my mom is one of my favorite computer-related childhood memories. Yet they seemed to go out of style long before Flash did.

Re:It's always been you, Canvas2D by StillBored on Friday March 04, 2022 @01:11PM (#30558239)
Some of this has got to be the fact that macromedia flash originally was more targeted to artists and it was dead simple to create nice 2d animations. AKA it wasn't a programming environment so much as an artistic tool. The current version (adobe animate) doesn't seem nearly as popular despite the fact that it can apparently target html/canvas. Part of that just might be fragmentation, with wick editor, opentoonz, there isn't a single tool everyone talks about. Although I'm not sure if those tools can legitimately "grow" into programming tools the way flash and now animate can. (aka the artist draws up a bunch of stuff and then needs to learn some programming or have a programmer to come along and fix the donate button so it actually takes money like was possible with flash).

OTOH, I used to know a bunch of artistic types that had pirated copies and would sit around and create flash animations for their bands/etc. Now those people just do video editing and post things to youtube/etc, or spend time editing photos for facebook/etc. There isn't a hypercard/programming aspect anymore buried under their tooling.

edit: actually it looks like wick can do the hypercard/flash programming too, which is sorta cool.

Re:It's always been you, Canvas2D by thrashh on Friday March 04, 2022 @01:42PM (#30558673)
Distributing a Flash file was dead easy

Distributing a HTML file and its assets? God damn complex

Re:It's always been you, Canvas2D by tentacleuno on Friday March 04, 2022 @05:56PM (#30561744)
> Distributing a HTML file and its assets? God damn complex

There's always Web Bundles[0], but I have no idea what the UX is like. I haven't heard about Web Bundles for a while, but they seem like such a good idea.

[0]: https://web.dev/web-bundles/

Re:It's always been you, Canvas2D by gnramires on Wednesday March 16, 2022 @02:04PM (#30702011)
I think p5.js is emerging quickly among artists!
Re:It's always been you, Canvas2D by andai on Friday March 04, 2022 @10:21PM (#30563886)
I think this is largely (entirely?) a failure of marketing on Adobe's part. I spoke to several people who created flash animations and games in the 2000s and they thought Flash-the-tool had died along with Flashplayer, they didn't know it had been rebranded Animate.
Re:It's always been you, Canvas2D by conductr on Friday March 04, 2022 @12:06PM (#30557370)
My recollection of Flash was it was just an annoyance. I remember avoiding flash websites with the exception being games. And I’m not a huge gamer but I recall a handful of flash games that stole a few days of attention from me. I always find it a bit odd when I hear people have positive nostalgic memories of flash because I had thought everyone equally agreed it was annoying: similar to Java applets, the Real Player, and actually many JavaScript things at the time.
Re:It's always been you, Canvas2D by benrbray on Friday March 04, 2022 @12:15PM (#30557471)
However much Flash was abused on the web, it was an absolute joy to create games and animations with. There aren't any tools I know of today that allow for the same rapid art+animation+code workflows. I've tried using Canvas2D+JS as well as SVG+JS to do some of the same things, and they don't even come close to Flash!
Re:It's always been you, Canvas2D by conductr on Friday March 04, 2022 @12:25PM (#30557613)
Interesting account. I was diving into tech around turn off the millennia and I remember spending all of a day or so in action script and went back to the server side stuff. There’s so much game content around I have to believe the void is being met somewhere? Was this just an easy on ramp for low/no experienced folks? I believe that’s how the dreamweaver and other connected adobe products were positioned.
Re:It's always been you, Canvas2D by thrower123 on Friday March 04, 2022 @01:37PM (#30558609)
Yeah, it's really not. That entire segment that Flash games owned doesn't exist any more in the same way that it did.

The closest thing would be the mobile games like Candy Crush and Garden Saga, but even there the tide has passed, and it's too difficult to make anything - King was bought by Activision for 6 billion dollars.

Re:It's always been you, Canvas2D by moron4hire on Friday March 04, 2022 @03:37PM (#30560192)
There were several major indie games that started out in Flash and ended up on consoles. Alien Homind, N++, and Castle Crashers come to mind, in particular. They were pretty great games. Castle Crashers on Xbox 360 was a favorite of my roommates.
Re:It's always been you, Canvas2D by conductr on Friday March 04, 2022 @06:04PM (#30561819)
> That entire segment that Flash games owned doesn't exist any more in the same way that it did.

Really? Or did move to a platform like App Store/Steam. I’ve seen it quantified and there’s a absurd amount of new games posted daily. I won’t argue that the tools in use are easier or better than flash, but developers certainly pivoted or so it seems. From a laymen/consumer perspective, Browsing the App Store doesn’t feel much different that I remember Kongregate or some of the other big flash game sites of the past.

Re:It's always been you, Canvas2D by robertoandred on Friday March 04, 2022 @12:40PM (#30557814)
The Flash tool still exists, Adobe calls it Animate now. It exports to canvas/js.
Re:It's always been you, Canvas2D by CountHackulus on Friday March 04, 2022 @01:42PM (#30558669)
The functionality isn't really the same. Yes, you can export animations, but it's much more difficult to provide the same level of interactive game that Flash did.
Re:It's always been you, Canvas2D by claytongulick on Friday March 04, 2022 @02:23PM (#30559169)
Either way, the death of AS3 was a sad thing.

One of the best languages ever made, imho.

Optional typing, e4x, normal inheritance (non-prototypal), etc...

The web would be a better place if AS3 took over instead of js (and I love js).

I imagine wasm would have shown up and been a ton easier to implement as well. AOT bits that are full static, JIT bits that aren't. No need for TypeScript, Flow, etc...

Especially sad since iirc Adobe donated the entire thing and was pushing for it to become standard in HTML4.

Re:It's always been you, Canvas2D by titzer on Friday March 04, 2022 @01:37PM (#30558607)
I think Flash died 2 years too early, or WebAssembly came 2 years too late. Because it always seemed to me that Flash was a good idea for the web, if it existed as a layer that was not "native" could be linked as a library (on top of Wasm and canvas). Alas, there were plans to emulate Flash using Wasm, but it was on its deathbed by the time we were starting the open design process. Adobe's VM seems to have had some design decisions that made it difficult to just "hit the button" and make it run decent on Wasm.
Re:It's always been you, Canvas2D by claytongulick on Friday March 04, 2022 @02:30PM (#30559280)
I think this can be laid at the feet of Apple.

I loved Jobs' vision of HTML5 being the app platform, and getting rid of flash.

Instead he got voted down, and we got the early death of flash with the replacement being a walled-garden app store, setting the web back for years.

They're starting to get their act together (I'm looking at you Web Push Notifications) but it's been a long, painful road.

Re:It's always been you, Canvas2D by multiplegeorges on Friday March 04, 2022 @01:55PM (#30558819)
Flash was huge for playful things because it was extremely accessible.

Say what you want about the underlying tech, the authoring tools were easy for a kind (like me, at the time) to pick up and make animations with.

Is there an equivalent for Canvas?

Re:It's always been you, Canvas2D by munificent on Friday March 04, 2022 @12:17PM (#30557509)
I think, fundamentally, delight and whimsy don't scale.

They are based on some element of surprise. Once the surprise is gone because you've seen it a few times, the joy evaporates and then it's just annoying.

Re:It's always been you, Canvas2D by danielvaughn on Friday March 04, 2022 @02:54PM (#30559555)
I can understand where that sentiment comes from, but I think what really killed the playful flash web was accessibility. It's difficult enough to be fully accessible for a normal website; anything out of the ordinary is going to be significantly more difficult.
Re:It's always been you, Canvas2D by leeoniya on Friday March 04, 2022 @12:40PM (#30557813)
> context.reset is kinda funny. Most high-performance canvas apps will never want to use it. For that matter you want to set all properties as little as possible, especially setting things like context.font, which are slow even if you're setting it to the same value. (Or it was, I haven't tested that in several years).

yes, ctx.font mutation still sucks very badly. and you cannot avoid it during drag-resizing a canvas, which is miserable. (i maintain a high perf charting lib).

my additional annoyances: https://news.ycombinator.com/item?id=30554387

Re:It's always been you, Canvas2D by Trufa on Friday March 04, 2022 @12:17PM (#30557502)
I actually do if you have the time thanks!

Whenever I'm trying to draw curved lines, I always have issues with how they look, they seem to look low quality/pixelated.

Like here: https://developer.mozilla.org/en-US/docs/Web/API/CanvasRende...

In the most basic examples they seem to always produce curves like this.

Sorry I don't have more specifics since it's a project I'm not actively working on right now but it was a spine that I never removed.

If the questions is too broad, please let me know!

Re:It's always been you, Canvas2D by simonsarris on Friday March 04, 2022 @12:30PM (#30557680)
99% the problem is one of pixel density. That is, you have a 400x400 canvas, and a 400x400 CSS pixel space you are drawing it to, but your devices pixel ratio is higher than 1, so it looks kinda blurry. This is because devices squish more than one "hardware pixel" per CSS pixel. Often at a ratio of 1.5 or 2, or higher.

The solution is to make a larger canvas, say, 800x800 and put it into that 400x400 space.

Here is an example, using that MDN code, with a 400x400 canvas (red) next to a 800x800 canvas (blue). CSS is forcing them both to appear the same 400x400 size. The blue one should look sharper on most devices.

Note how the 800x800 canvas needs to be scaled double with ctx2.scale(2,2) so that it appears correct.

https://codepen.io/simonsarris/pen/eYexbOb

Pixel ratio is variable (window.devicePixelRatio), so this canvas pixel density is something you'll want to programmatically set for each user.

Re:It's always been you, Canvas2D by Trufa on Friday March 04, 2022 @12:35PM (#30557741)
Dude!! Freaking awesome!! Thanks a bunch!! That's exactly what I was looking for! If you have some sort of ko-fi account or whatever I'll buy you a coffee!
Re:It's always been you, Canvas2D by etimberg on Friday March 04, 2022 @01:09PM (#30558221)
One thing to keep in mind with the window.devicePixelRatio is that it is not a static value as it can change over time. Some common reasons it could change are if the user drags the browser window from a low density screen to a high density one. Additionally, it will change if the user zooms the page in and out.
Re:It's always been you, Canvas2D by leeoniya on Friday March 04, 2022 @02:33PM (#30559318)
you can use matchMedia to detect devicePixelRatio changes and re-sync:

https://github.com/leeoniya/uPlot/blob/190134aa844cfa2a0c052...

everything stays crisp even as you browser-zoom. e.g. https://leeoniya.github.io/uPlot/demos/area-fill.html

Re:It's always been you, Canvas2D by moron4hire on Friday March 04, 2022 @03:20PM (#30559937)
Zooming also fires the Window's `resize` event, which will also catch a user rotating their smartphone/tablet, or, erh, resizing the window on desktop.
Re:It's always been you, Canvas2D by leeoniya on Friday March 04, 2022 @03:43PM (#30560276)
resize and orientationchange don't change devicePixelRatio though, so they're somewhat different concerns.
Re:It's always been you, Canvas2D by moron4hire on Friday March 04, 2022 @07:04PM (#30562326)
No, it's the same concern. The concern is not "how do I keep track of just devicePixelRatio?" It's "how do I fill this window with a canvas at native resolution?" Resize handles all of that. Device changes orientation? Resize fires. User zooms on content (this changing devicePixelRatio)? Resize fires. Anything that changes the page's clientWidth/clientHeight causes the Resize event to fire. You need only the one event.
Re:It's always been you, Canvas2D by Dylan16807 on Friday March 04, 2022 @09:39PM (#30563597)
> The concern is not "how do I keep track of just devicePixelRatio?" It's "how do I fill this window with a canvas at native resolution?"

Not everyone's canvas is filling the window.

Re:It's always been you, Canvas2D by moron4hire on Friday March 04, 2022 @10:32PM (#30563956)
All the ways in which you will care about how to buffer dimensions of your canvas to avoid aliasing artifacts can be handled with just the window.onresize event, whether your canvas is fixed or proportionally sized.

https://codepen.io/SeanMcBeth/pen/QWOoLNX

Throw a check in there to make sure the native dimensions actually did change so you don't needlessly redraw. Debounce it with setTimeout if you're super worried about people waggling their windows around or whatever.

Re:It's always been you, Canvas2D by inDigiNeous on Friday March 04, 2022 @03:27PM (#30560046)
Yeah I was struggling with this one a while, seems there is no way to know the actual devicePixelRatio of the display inside a browser, as the browser zoom level directly affects that.

Or does anyone here happen to know a way ?

Re:It's always been you, Canvas2D by moron4hire on Friday March 04, 2022 @07:29PM (#30562521)
DevicePixelRatio and clientWidth/clientHeight change together. You need only the Resize event on the Canvas. Your canvas.clientWidth * window.devicePixelRatio will always be the native width of the canvas.
Re:It's always been you, Canvas2D by simonsarris on Friday March 04, 2022 @01:01PM (#30558110)
Haha well, you could always subscribe to my newsletter for a month. You might get a kick out of it anyway.

I'm glad to have solved your mystery!

Re:It's always been you, Canvas2D by Trufa on Saturday March 05, 2022 @09:19AM (#30567798)
Done!!
Re:It's always been you, Canvas2D by altairprime on Friday March 04, 2022 @02:12PM (#30559037)
To add a small caveat here for others, assuming 2x as the necessary scaling multiplier is not necessarily a safe shortcut when choosing your virtual canvas size; iPhones use 3x resources to look crisp, and 5K macOS users can have their 5120x2880 displays set to 2048x1152 or 1600x900 which are 2.5x and 3.2x respectively.

It still helps when you do your canvas at 2x rather than just at 1x. But if you're looking for best-fidelity rather than better-fidelity, duly noted.

Re:It's always been you, Canvas2D by jorl17 on Friday March 04, 2022 @09:54PM (#30563693)
I recently embarked on a months-long just-for-fun effort of porting an old AS3/Flash game to Haxe and make sure it worked on every imaginable Haxe target I could find. If there was something that bit me was how ugly everything looked inside the browser, even when using a WebGL backend.

It turns out it was very obviously exactly what you described. You basically have to double the resolution to get it to look decent on retina and 4k screens. Once I found this out I was over the moon!

(On a related note, colouring looks way off on the HTML5 target compared to native ones, and I spent an embarassing number of hours manually brute-forcing a shader to make everything look similar.

Oh, and sound sucks in the browser.

Oh, and Safari sucks. Seriously, it just sucks. The amount of hacks I put in place to workaround its egregious limitations, particularly regarding canvas operations is insane.)

Re:It's always been you, Canvas2D by ngcc_hk on Friday March 04, 2022 @09:10PM (#30563375)
Wow. Great hint. Thanks.
Re:It's always been you, Canvas2D by mpmpmpmp on Friday March 04, 2022 @09:00AM (#30554796)
What would your strategy for working with raw image data and implementing a digital “zoom” that doesn’t do any anti aliasing or smoothing? My current thought is to just have an original copy of the data and then the current canvas is just a smaller subset of the original data when zoomed. So a 100x100 pixel image when zoomed 1x would take a 50x50 subset based on the center of the viewport and copy each pixel into a 2x2 to get back to a 100x100 image.
Re:It's always been you, Canvas2D by simonsarris on Friday March 04, 2022 @10:14AM (#30555941)
Unless I misunderstand you, you should be able to `ctx.drawImage` from one canvas to another, with `imageSmoothingEnabled` set to false, and it will do what you want.

https://developer.mozilla.org/en-US/docs/Web/API/CanvasRende...

Re:It's always been you, Canvas2D by hutzlibu on Friday March 04, 2022 @10:22AM (#30556027)
"One of the greatest things to happen to 2D canvas in all these years was hardware acceleration."

I loved this one, too. Except for the one drawback, getImageData is now very, very slow. And no asynchronous method in sight, that could make it fast again.

Re:It's always been you, Canvas2D by dahart on Friday March 04, 2022 @11:25AM (#30556840)
How slow is it these days? Even readPixels() in OpenGL and hw buffer copies in C++ are “slow” because you’re transferring the image data over your PCI bus.

Also curious how & whether an async method could help? Is the problem that getImageData() has to wait to flush all pending draw calls before reading back? Are you thinking of some kind of callback that returns the previous frame’s data, rather than demanding it ‘now’? There probably is room for perf improvement, totally, just curious how you’re imagining it would look and if you have data on where the current bottlenecks are.

Re:It's always been you, Canvas2D by kllrnohj on Friday March 04, 2022 @11:35AM (#30556969)
PBOs allow for asynchronous readbacks from opengl. It's not slow because of the pcie bus (heck for most devices this is unified memory in the first place, it doesn't hit any bus other than dram). Rather it's slow for the same reason glFinish() is. You're blocking waiting for the results of the asynchronous rendering which you probably also just kicked off right at that point.

Also it's not like the opengl is happening on the same thread as the canvas2d (or even the same process), so that's yet more synchronization that a blocking getImageData needs to wait on.

Re:It's always been you, Canvas2D by dahart on Friday March 04, 2022 @11:48AM (#30557147)
Yeah, exactly, that’s what I’m curious about - if the browser’s renderer is adding significant amounts of delay due to additional synchronization, or if the problem with canvas.getImageData() is the same problem we’ve always had and if the delays are similar. This does make it seem like a callback to receive last frame’s image could be much faster than a synchronous read call, and I’d certainly use that if it existed…
Re:It's always been you, Canvas2D by kllrnohj on Saturday March 05, 2022 @05:05PM (#30572402)
canvas.getImageData() would have gotten a lot slower as GPU acceleration of Canvas2D became increasingly more common. The initial versions of Canvas2D were all software rasterized, which means getImageData() likely didn't involve any IPCs and definitely didn't involve any GPU readbacks. It was just a memcpy if that.
Re:It's always been you, Canvas2D by hutzlibu on Friday March 04, 2022 @12:00PM (#30557282)
"How slow is it these days?"

So slow, that chrome falls back to software rendering, if you call getImageData 3 frames in a row.

(and does not even switch back to hardwarerendering if you stop calling it - last time I checked, was some months ago, so maybe that has improved)

Re:It's always been you, Canvas2D by adam_arthur on Friday March 04, 2022 @05:53PM (#30561718)
This post implies you can mark a canvas as "read frequently" to improve getImageData.

I'm excited to try it out, as I do a kind of hacky hit detection using a hidden canvas, And reading the pixel color is certainly the bottleneck there

Re:It's always been you, Canvas2D by inDigiNeous on Friday March 04, 2022 @03:25PM (#30560019)
Interesting to hear about that doing the matrix translations on the js side are faster. Have you benchmarked this recently ? Will have to test it out myself, was under the naive impression that the underlying implementation would be optimal.
Re:It's always been you, Canvas2D by simonsarris on Friday March 04, 2022 @03:39PM (#30560227)
Imagine something like:

    var transform = ...your own data structure
    transform.scale(zzz);
    transform.rotate(zzz);
    transform.translate(zzz);
    transform.scale(zzz);
    // perhaps as many as 50 as you go down the visual tree
    ctx.setTransform(use your data structure to set these values);
This is a little faster than calling all the corresponding methods on ctx. Of course you gotta implement a transform class.

And of course it becomes meaningfully faster if you are drawing a large number of objects in a visual tree, and their locations do not all change, so you can save these transforms that you've created, and then the only thing you are doing in the draw loop is a single call to `ctx.setTransform` instead of all the calls you'd normally need. Again this really depends on what you're doing, but you can imagine for drawing the visual tree of a diagram, it can be quite an improvement.

Re:It's always been you, Canvas2D by dmitriid on Friday March 04, 2022 @09:19AM (#30555076)
The problem is: browser implementors are never/rarely website implementors. So you always end up with half-baked tangentially useful standards and APIs like this. They just don't know what is actually needed.
Re:It's always been you, Canvas2D by mysteryDate on Friday March 04, 2022 @09:24AM (#30555154)
Well, there's this and the fact that browser implementers all need to come to consensus over what features to add and that's an extremely difficult process to move forward. There also is one big browser implementer who doesn't really want the "playful web" to exist and would prefer that everyone live inside of apps.

Edit: To be clear, we all work together and mostly get along. It's a long and arduous process to reach consensus and not everyone's incentives are aligned and this can be frustrating.

Re:It's always been you, Canvas2D by robertoandred on Friday March 04, 2022 @12:46PM (#30557903)
Your post implies that Google cares about browser consensus. For example, "This feature has been in Firefox for a while and we're finally making it part of the canvas spec." Google DOES NOT decide what the spec is. "You" don't make something part of the spec.

The Chrome team's hubris is insulting to the web.

Re:It's always been you, Canvas2D by dmitriid on Friday March 04, 2022 @09:47AM (#30555521)
> There also is one big browser implementer who doesn't really _want_ the "playful web" to exist and would prefer that everyone live inside of apps.

No. There's one humongous browser implementer who couldn't care less about consensus or what other browser vendors think. This vendors ships literally hundreds of new APIs every year and pretends they are now standards that everyone else must implement.

Too bad developers believe them.

BTW, it only took only 8 minutes for my countdown to reach zero: https://news.ycombinator.com/item?id=30555034

Re:It's always been you, Canvas2D by robertoandred on Friday March 04, 2022 @12:56PM (#30558024)
Don't you know? The "playful web" means websites getting access to your USB devices or being able to keep your screen from sleeping/locking!
Re:It's always been you, Canvas2D by dmitriid on Friday March 04, 2022 @10:24AM (#30556054)
Someone linked "Request for position" on these APIs: https://github.com/mozilla/standards-positions/issues/519#is...

Here's Mozilla's response:

--- start quote ---

4x4 transforms:

There are a bunch of implementation concerns here (and mentioned on that issue) with regards to availability on various native 2d backends... Needs more investigation

SVG filter interface:

We would really rather people use WebGL if you want fast/efficient filters. (I made a number of comments on that issue) As is, we're generally against this one for the time being

--- end quote ---

There's literally zero response on that from @mysteryDate who is now gaslighting Safari in the comment above, and presenting these API additions as fait accompli.

Honestly, at this point any time I see any public Chrome person write anything I immediately assume it's a distortion of reality at best and a blatant lie at worst. And this is always the case.

But sure. "Safari is the bad guy".

Re:It's always been you, Canvas2D by mysteryDate on Friday March 04, 2022 @10:47AM (#30556354)
There was a ton of work across browser vendors to make this a part of spec:

https://html.spec.whatwg.org/multipage/canvas.html#the-canva...

It's all there. It's all official. That github page was just one part of reaching consensus. There's also TAG review:

https://github.com/w3ctag/design-reviews/issues/627

FWIW Mozilla and Safari signed off on all of these changes at some point in time somewhere, hence why it's allowed to be part of spec. There were some changes that were not allowed to be part of the new API because one of those two said no (like perspective transforms, conic curves).

For jdashg's concerns on that thread, 4x4 matrices were cancelled, and you can follow up with much more debate from all parties on roundRect and filters:

http://github.com/whatwg/html/pull/6763 https://github.com/whatwg/html/pull/6765

This is certainly not decided by fiat. Working to find consensus across browser implementers is just a ton of work.

Re:It's always been you, Canvas2D by dmitriid on Friday March 04, 2022 @01:33PM (#30558553)
> It's all there. It's all official.

> FWIW Mozilla and Safari signed off on all of these changes at some point in time

That's a relief then. Too often these days "it's official it's in the spec" as presented by Chrome is anything but.

> This is certainly not decided by fiat.

There are too many cases when it's decided unilaterally by Chrome.

Re:It's always been you, Canvas2D by rikroots on Friday March 04, 2022 @11:04AM (#30556571)
> We would really rather people use WebGL if you want fast/efficient filters.

This one made me laugh. Yes, WebGL excels at pixel manipulations but it is possible to write fast and efficient filters to work in the 2D canvas environment.

For a case-in-point, I struggled for a long time to find a decent, fast implementation of a gaussian blur filter for my canvas library. Then I stumbled upon a JS implementation[1] based on some very clever work done by Intel devs which blew all my previous attempts out of the water - so of course I stole it (even though I still don't understand the approach they take)[2].

> "Safari is the bad guy"

As much as Safari often brings me to despair, I do like the work they've recently done to add color space support in CSS. They haven't yet pushed the functionality over to the canvas element, but I live in hope. For now, I have to emulate the calculations to get them working for my library[3].

[1] - https://github.com/nodeca/glur/blob/master/index.js

[2] - https://scrawl-v8.rikweb.org.uk/docs/source/factory/filterEn...

[3] - https://scrawl-v8.rikweb.org.uk/demo/canvas-059.html

Re:It's always been you, Canvas2D by mysteryDate on Friday March 04, 2022 @11:11AM (#30556670)
Oh yeah! Safari is definitely doing great for colorspaces and we have some work to do to catch up. Very good point.
Re:It's always been you, Canvas2D by DonHopkins on Friday March 04, 2022 @02:37PM (#30559350)
Cool, interesting links to code -- thank you! I chased down Intel's paper the code linked to describing how it works on archive.org.

https://web.archive.org/web/20110317025924/https://software....

It's not just about using SIMD instructions (they help), and laying out memory to optimize cache performance (which also helps), but most importantly that Gaussian blur is a "separable filter" that you can break up into a horizontal and vertical pass, each of which require a lot fewer memory references (on the order of just two times the number of pixels times the kernel size, instead of the number of pixels times the kernel size squared):

IIR Gaussian Blur Filter Implementation using Intel® Advanced Vector Extensions

>This white paper proposes an implementation for the Infinite Impulse Response (IIR) Gaussian blur filter [1] [2] [3] using Intel® Advanced Vector Extensions (Intel® AVX) instructions. [...]

>The IIR Gaussian blur filter applies equation (1) on each pixel through two sequential passes: The horizontal pass: This pass processes the input image left-to-right (row-wise), then right-to-left. The output of the left-to-right pass is added to the right-to-left pass.

>The vertical pass: Usually, the vertical pass processes the output from the horizontal pass top-to-bottom (column-wise), and then bottom-to-top. Accessing the input column-wise leads to a lot of cache blocks and impacts the performance of the filter. To avoid this, the horizontal pass transposes the output before writing to the output buffer. It makes the vertical pass similar to the horizontal pass and processes the intermediate output left-to-right, then right-to-left. The vertical pass again transposes the final output before writing the blurred image.

https://bartwronski.com/2020/02/03/separate-your-filters-svd...

>Separate your filters! Separability, SVD and low-rank approximation of 2D image processing filters Posted on February 3, 2020 by bartwronski

>In this blog post, I explore concepts around separable convolutional image filters: how can we check if a 2D filter (like convolution, blur, sharpening, feature detector) is separable, and how to compute separable approximations to any arbitrary 2D filter represented in a numerical / matrix form. I’m not covering any genuinely new research, but think it’s a really cool, fun, visual, interesting, and very practical topic, while being mostly unknown in the computer graphics community.

https://en.wikipedia.org/wiki/Gaussian_blur#Mathematics

>In addition to being circularly symmetric, the Gaussian blur can be applied to a two-dimensional image as two independent one-dimensional calculations, and so is termed a separable filter. That is, the effect of applying the two-dimensional matrix can also be achieved by applying a series of single-dimensional Gaussian matrices in the horizontal direction, then repeating the process in the vertical direction. In computational terms, this is a useful property, since the calculation can be performed in O(w_kernel w_image h_image) + O(h_kernel w_image h_image) time (where h is height and w is width; see Big O notation), as opposed to O(w_kernel h_kernel w_image h_image) for a non-separable kernel.

Re:It's always been you, Canvas2D by IceDane on Friday March 04, 2022 @09:51AM (#30555592)
Your library seems absolutely great. I can't believe I've never stumbled upon it before since I've literally gone looking for stuff exactly like this several times and never found anything very good.
Re:It's always been you, Canvas2D by david927 on Friday March 04, 2022 @12:12PM (#30557439)
HN hates "me too" comments but I couldn't agree more. Absolutely stunning job. Great work. I don't know how I didn't know it existed before.
Re:It's always been you, Canvas2D by DonHopkins on Friday March 04, 2022 @02:02PM (#30558915)
Excellent advice! Your setmatrix advice is unintuitive, but I believe you. The JavaScript/C barrier is expensive, while JavaScript calling JavaScript is optimized up the yin-yang.

I love canvas because I cut my teeth on PostScript, and it's basically the PostScript imaging model grown up.

What you said about minimizing graphics state changes is an important point, that also applies to PostScript. And a lot of PostScript optimization advice also applies to canvas/JavaScript too, because they're similar in many ways.

Glenn Reid (who worked for Adobe, and was the author of the "Green Book" on PostScript language program design) wrote "The Distillery", which I've written about before on HN (link and excerpt below), that was a PostScript program that loaded in and partially evaluated another PostScript drawing program, and output another program that drew the exact same thing, only (usually) much faster and usually smaller. Unless you had any loops, which it would unroll.

https://www.donhopkins.com/home/archive/news-tape/utilities/...

One of the most important optimizations it did was to transform all the graphics into the same default coordinate system, and optimize out not only graphics state changes but also importantly calls to gsave/grestore, which could be quite common.

https://news.ycombinator.com/item?id=21988195

Glenn Reid described his classic "Grandfather Clock" metaphor to comp.lang.postscript that really opened my eyes about writing code in interpreted languages like PostScript (and JavaScript, while JIT'ed at runtime, still makes calling native code expensive), and balancing optimization with readability. With modern JavaScript/canvas, JavaScript code that calls other JavaScript code runs really fast, but calling between JavaScript and built-in code is still slow, so it's good to have the built-in code do as much as possible when you do (like rendering a long string of text at once, instead of rendering it word by word like many PostScript drivers would):

Glenn's post in a comp.lang.postscript discussion about PostScript programming style and optimization:

https://groups.google.com/forum/#!search/%22glenn$20reid%22$...

    From: Glenn Reid (Abode Systems)
    Newsgroup: comp.lang.postscript
    Subject: Re: An Idea to Help Make Postscript Easier to Read (and Write)
    Date: 10 Sep 88 17:26:24 GMT

    You people tend to forget that the PostScript language is interpreted.
    It is well and good to use tools to convert to and from PostScript,
    but it is not quite as "transparent" as we all might think.
    I like to think of a big grandfather clock, with the pendulum swinging.
    Each time pendulum swings, the PostScript interpreter gets to do one
    operation.  The "granularity" of the clock is nowhere near the speed
    of a microprocessor instruction set, and any comparison with assembly
    languages doesn't make sense.

    The difference between:

            0 0 moveto

    and

            0 0 /arg2 exch def /arg1 exch def arg1 arg2 moveto

    can sort of be measured in "ticks" of the interpreter's clock.  It's
    not quite this simple, since simply pushing a literal is faster than
    executing a real PostScript operator, but it is a rough rule of thumb.
    It will take about three times as long to execute the second of these
    in a tight loop, and about five times as long if it is transmitted and
    scanned each time.  My rule of thumb is that if you have roughly the
    same number of tokens in your stack approach as you do with your 'exch
    def' approach, the 'exch def' is likely to be much more readable and
    better.  Otherwise, I usually go with the stack approach.

    One other thing of note is that if you have too much stack manipulation
    going on, it may well be symptomatic of a problem in the original program
    design.

    Also, most procedures don't do any stack manipulation at all, they
    simply use their arguments directly from the stack.  In this situation,
    it is especially wasteful (and confusing, I think) to declare
    intermediate variables.

    Compare:

    % sample procedure call:
            (Text) 100 100 12 /Times-Roman SETTEXT

    % approach 1:
            /SETTEXT { %def
                findfont exch scalefont setfont moveto show
            } def

    % approach 2:
            /SETTEXT { %def
                /arg5 exch def
                /arg4 exch def
                /arg3 exch def
                /arg2 exch def
                /arg1 exch def
                arg5 findfont arg4 scalefont setfont
                arg2 arg3 moveto arg1 show
            } def

    Which of these is easier for you to understand?

    Anyway, I think the discussion is a good one, but let's not forget
    that PostScript it is an interpreted language.  And I don't think
    it is terribly hard to use and understand, if it is written well.

    Glenn Reid
    Adobe Systems

Here's Glenn's "Green Book", which was like a bible to me, and still is quite relevant to canvas 2d context programming -- see especially page 9, section 1.5, Program Design Guidelines, page 72, section 4.6, Optimizing Translator Output, and page 99, chapter 7, The Mechanics of Setting Text:

https://www-cdf.fnal.gov/offline/PostScript/GREENBK.PDF

>page 9: 1.5 Program Design Guidelines

>There are a few items that may be kept in mind while implementing a driver for a PostScript device. As with most software development, the most difficult part of writing programs in the PostScript language is the design of the program. If the design is good, implementing it is easy. If the design is poor, it may not even be possible to correctly implement it. Below are some helpful items to keep in mind when writing your software. All of them are explained more fully within the text of this book; this is only an attempt to prime the pump before you start reading:

>• Use the operand stack efficiently. Pay particular attention to the order of the elements on the stack and how they are used.

(Using the stack efficiently by designing fluent words that chain and dovetail together elegantly in pipelines (and systematically writing line-by-line stack comments) instead of using named variables in dictionaries is good idiomatic PostScript and Forth, aka tacit programming or point-free style, the stack-based equivalent of fluent interfaces.)

https://en.wikipedia.org/wiki/Tacit_programming

https://en.wikipedia.org/wiki/Fluent_interface

>• Avoid unnecessary redundancy. When a program is produced, check for many repetitive steps that perhaps could be condensed. Keep identifiers short if they are to be transmitted many times.

(Only create paths once!)

>• Use the PostScript imaging model effectively. When printing, a document must be translated into the language of the printer. This includes a philosophical adjustment to the nature of the PostScript imaging model.

(Use the graphics state stack!)

>• It is better to translate into the PostScript imaging model than to maintain another set of graphics primitives using the PostScript language for emulation.

The hardest problem I ever tried (and failed) to solve properly with PostScript was making a printer driver for rendering user interfaces drawn with X11 using a combination of bitmaps and lines, that looked perfect on the screen at 1:1 scale, but didn't look terrible when you zoomed into them or printed them at high resolution on paper. Because of X11 "half open" pixel rounding rules versus PostScript's "stencil paint" model, they just don't line up right when you zoom into them, and there's no fudge or compromise that works in all cases. Here is my commented-out failed attempt:)

https://github.com/mmontone/garnet/blob/1652af38f76b1c4efb19...

    line-color line-cap line-join dash-pattern
    thickness
    % dup -1 ne { .5 add } if % fudge outline width thicker
    StrokeShape
>page 100: Note: There is one principle to keep in mind when deciding upon an algorithm for setting text. The longer the string presented to one of the show operators, the more efficient the system is likely to be. This is because the PostScript language built-in operators, such as show, widthshow, and ashow, operate essentially at compiled speed once they have been invoked. Each moveto or div operation performed must first be interpreted, which is significantly slower.

Here's a description of Glenn's PostScript Distillery, which foreshadowed Acrobat Distiller.

https://news.ycombinator.com/item?id=28115946

>Glenn Reid wrote a PostScript partial evaluator in PostScript that optimized other PostScript drawing programs, called "The Distillery". You would send still.ps to your PostScript printer, and then send another PostScript file that drew something to the printer. The first PostScript Distillery program would then partially evaluate the second PostScript drawing program, and send back a third PostScript program, an optimized drawing program, with all the loops and conditionals unrolled, calculations and transformations pre-computed, all in the same coordinate system.

>It was originally John Warnock's idea, that Glenn implemented. And it led to Adobe Acrobat's "Distiller". Acrobat is basically PostScript without the programming language.

>No, you could not make it optimize itself by sending it to a PostScript printer two times in a row. It was not magic: all it did was intercept and capture the side-effects of the PostScript drawing commands (fill, stroke, show), read out the path, and optimize it in a uniform coordinate system. Since it didn't do any drawing, so it would just output an empty program if run on itself. (Take that, halting problem!)

https://donhopkins.com/home/archive/postscript/newerstill.ps...

>From: [email protected] (Glenn Reid) Newsgroups: comp.lang.postscript Subject: release 10 of the Distillery Date: 10 Mar 89 10:21:52 GMT

>Here is another release of the PostScript Language Distillery. I know it's not terribly long after the last release, but there are some significant enhancements, and I thought it would be worthwhile.

>I finally took a closer look at user-defined fonts, which now seem to be working fairly well. In particular, it seems to handle the Macintosh screen bitmap fonts that get used if the native font is unavailable when the print file is genreated. The entire user-defined font is reverse-engineered to the output file as it stands, and is used exactly like the original file used it. I also fixed some rotate text bugs, rotated charpath, and a few other things.

>I want to emphasize that probably the two best uses of this program, currently, are to do speed comparisons of various PostScript language drivers and to convert "non-conforming" files into "conforming" files. It is not particularly well suited to carefully hand-written programs, especially not those which use looping constructs. It works (usually), but it unrolls the loops and makes the files much bigger.

Re:It's always been you, Canvas2D by c_s_guy on Friday March 04, 2022 @02:14PM (#30559066)
> Though you don't need 4 arcTo in order to make a rounded rect, you can use bezier instead (we do)

I'm pretty new to this - do you have an example of using bezier? And why is it preferred over arcTo?

Re:It's always been you, Canvas2D by ygra on Saturday March 05, 2022 @03:06AM (#30565465)
You can use cubic Bézier curves to approximate a circular arc: https://pomax.github.io/bezierinfo/#circles_cubic. In many cases that's even how circular arcs are rendered, even when they are represented with a different data structure. You'll often see the the constant 0.5522... which is for quarter circles. E.g. both WPF and Java's Swing render arcs with that approximation, perhaps others, but those where the places where I checked the source code recently.

The benefit of doing this for quarter-circle arcs directly may be that you save a bunch of trigonometry that otherwise has to happen (cf. the Pomax link).

Re:It's always been you, Canvas2D by leeoniya on Friday March 04, 2022 @02:28PM (#30559255)
i think you'd still need 4 bezierCurveTo commands, so not sure there's any advantage over 4 arcTo:

    function roundedRect(ctx, x, y, width, height, radius) {
      ctx.beginPath();
      ctx.moveTo(x, y + radius);
      ctx.arcTo(x, y + height, x + radius, y + height, radius);
      ctx.arcTo(x + width, y + height, x + width, y + height - radius, radius);
      ctx.arcTo(x + width, y, x + width - radius, y, radius);
      ctx.arcTo(x, y, x, y + radius, radius);
      ctx.stroke();
    }
https://developer.mozilla.org/en-US/docs/Web/API/Canvas_API/...
Re:It's always been you, Canvas2D by tuckerpo on Friday March 04, 2022 @12:36PM (#30557755)
hey it's the twitter homestead photography guy. nice house, nice software too.