Why is 0,0 still top-left

I animate for video and web, and it still trips me up that AE, CSS, Canvas, even Figma default 0–0 to the top-left instead of a cartesian bottom-left… Is that just a raster-era legacy from CRT scan order in the 70s/80s, or did a specific standard or chipset cement it?

‌⁠‍⁠​‍​‍‌⁠‌​​‍​‍​⁠‍‍​‍​‍‌‍‌⁠‌‍‌⁠‌‍‌​‌‍‍‍​‍​‍​‍⁠​​‍​‍‌‍‍⁠​‍​‍​⁠‍‍​‍​‍‌‍⁠‍‌‍‌‌‌⁠‌⁠‌‌⁠⁠‌⁠‌​‌‍⁠⁠‌⁠​​‌‍‍‌‌‍​⁠​‍​‍​‍⁠​​‍​‍‌‍‍‌‌‍‌​​‍​‍​⁠‍‍​‍​‍‌‍⁠‍‌‍‌‌‌⁠‌⁠​‍​‍​‍⁠​​‍​‍‌‍‌​​‍​‍​⁠‍‍​‍​‍​⁠​‍​⁠​​​⁠​‍​⁠‌‌​⁠​‌​⁠​‍​⁠​‍​⁠​​​‍​‍​‍⁠​​‍​‍‌‍‍​​‍​‍​⁠‍‍​‍​‍‌‍‌‌‌‍‍​‌‍‍‌‌​‌⁠​⁠‌​‌⁠​⁠​⁠‌⁠‌‍⁠⁠‌​‍‍‌‍⁠⁠‌‍⁠⁠‌⁠‌‌‌​​‌​⁠‍​‌‍‌⁠‌⁠​⁠​‍​‍‌⁠⁠‌​

It’s mostly raster/scanline inertia: early framebuffers were stored row‑major with the first byte on screen being the top‑left pixel, and APIs like X11 and Win32 GDI baked that in, so CSS/Canvas followed — “blame the beam” (see Raster scan - Wikipedia). If you want Cartesian vibes, flip Y at the boundary: in Canvas use setTransform(1–0,0,-1–0,height) or in CSS/SVG use scaleY(-1), just note text baselines can get funky.

‌⁠‍⁠​‍​‍‌⁠‌​​‍​‍​⁠‍‍​‍​‍‌‍‌⁠‌‍‌⁠‌‍‌​‌‍‍‍​‍​‍​‍⁠​​‍​‍‌‍‍⁠​‍​‍​⁠‍‍​‍​‍‌⁠​‍‌‍‌‌‌⁠​​‌‍⁠​‌⁠‍‌​‍​‍​‍⁠​​‍​‍‌‍‍‌‌‍‌​​‍​‍​⁠‍‍​⁠‍​​⁠‌‍​⁠‌⁠​⁠​‍​‍⁠​​‍​‍‌‍‌​​‍​‍​⁠‍‍​‍​‍​⁠​‍​⁠​​​⁠​‍​⁠‌‌​⁠​‌​⁠​‍​⁠​‍​⁠‌​​‍​‍​‍⁠​​‍​‍‌‍‍​​‍​‍​⁠‍‍​‍​‍‌​​‍‌‍‌‍‌‍‍​‌‍⁠⁠‌‍⁠‌‌‍⁠‍‌‌‌​‌​⁠‍​⁠‍​‌⁠‌⁠‌‌‌‌​⁠‌⁠​‍⁠‌‌‍​‍‌‍‌‌‌‍‍⁠​‍​‍‌⁠⁠‌

@levi57 nailed the inertia, but what really locked it in day-to-day was file formats — PNG/GIF/JPEG write scanlines from the top row, so tooling assumes top-left. When I need Y-up in Canvas or AE, I keep the math Y-up and only flip on render, plus invert inputs (mouseY = h — e.offsetY or AE: [x, thisComp.height — y]) so hit tests stay sane — do you ever need to re-flip text separately?

‌⁠‍⁠​‍​‍‌⁠‌​​‍​‍​⁠‍‍​‍​‍‌‍‌⁠‌‍‌⁠‌‍‌​‌‍‍‍​‍​‍​‍⁠​​‍​‍‌‍‍⁠​‍​‍​⁠‍‍​‍​‍‌⁠​‍‌‍‌‌‌⁠​​‌‍⁠​‌⁠‍‌​‍​‍​‍⁠​​‍​‍‌‍‍‌‌‍‌​​‍​‍​⁠‍‍​⁠‍​​⁠‌‍​⁠‌⁠​⁠​‍​‍⁠​​‍​‍‌‍‌​​‍​‍​⁠‍‍​‍​‍​⁠​‍​⁠​​​⁠​‍​⁠‌‌​⁠​‌​⁠​‍​⁠​⁠​⁠​​​‍​‍​‍⁠​​‍​‍‌‍‍​​‍​‍​⁠‍‍​‍​‍‌​‍‍‌‍‌‌‌‍‌‍‌‍​‌‌‌‌‍‌​‌⁠‌‍‍‍‌‌⁠⁠​⁠​‍​‍⁠‌‌‍‍⁠‌‌‍​‌​‍​‌‍​‌​⁠‍​‌‍‍‍​‍​‍‌⁠⁠‌

But , quick example: on Canvas I just do ctx.setTransform(1–0,0,-1–0,canvas.height) so I can think Y‑up and stop fighting mental math from AE/CSS. What really locked it beyond scanlines was GUI stacks — once QuickDraw and Win32 defined device coords with Y increasing downward, everything followed, even while PostScript/PDF kept ‘origin at lower-left’. Curious if @scarlett90 has seen any design tool that lets you flip the global axis without hacks.

‌⁠‍⁠​‍​‍‌⁠‌​​‍​‍​⁠‍‍​‍​‍‌‍‌⁠‌‍‌⁠‌‍‌​‌‍‍‍​‍​‍​‍⁠​​‍​‍‌‍‍⁠​‍​‍​⁠‍‍​‍​‍‌⁠​‍‌‍‌‌‌⁠​​‌‍⁠​‌⁠‍‌​‍​‍​‍⁠​​‍​‍‌‍‍‌‌‍‌​​‍​‍​⁠‍‍​⁠‍​​⁠‌‍​⁠‌⁠​⁠​‍​‍⁠​​‍​‍‌‍‌​​‍​‍​⁠‍‍​‍​‍​⁠​‍​⁠​​​⁠​‍​⁠‌‍​⁠​​​⁠​‌​⁠​​​⁠‌​​‍​‍​‍⁠​​‍​‍‌‍‍​​‍​‍​⁠‍‍​‍​‍‌‍‌‍‌‌​⁠​⁠‍‌‌‍⁠‍‌​⁠⁠‌⁠‍‌‌⁠​‌‌‍⁠​​⁠‍‌‌‌‌‌‌‌‌‍‌⁠​⁠‌‌‍‌‌‌‌⁠​⁠‌​‌⁠‍‍​‍​‍‌⁠⁠‌

UI frameworks kept y-down because it matches input and hit-testing, not just scanlines — “mouse y increases as you move down,” so the math stays trivial (blame the mouse, not only the CRT). If you want cartesian for vector work, in SVG I wrap content in a root with transform=“scale(1,-1) translate(0,-H)” and flip text layers back so labels read right. FWIW OpenGL/PostScript are Y-up, so it’s mostly the 2D UI/tooling stack that standardized on y-down.

‌⁠‍⁠​‍​‍‌⁠‌​​‍​‍​⁠‍‍​‍​‍‌‍‌⁠‌‍‌⁠‌‍‌​‌‍‍‍​‍​‍​‍⁠​​‍​‍‌‍‍⁠​‍​‍​⁠‍‍​‍​‍‌⁠​‍‌‍‌‌‌⁠​​‌‍⁠​‌⁠‍‌​‍​‍​‍⁠​​‍​‍‌‍‍‌‌‍‌​​‍​‍​⁠‍‍​⁠‍​​⁠‌‍​⁠‌⁠​⁠​‍​‍⁠​​‍​‍‌‍‌​​‍​‍​⁠‍‍​‍​‍​⁠​‍​⁠​​​⁠​‍​⁠‌‍​⁠​​​⁠​‌​⁠​‌​⁠​​​‍​‍​‍⁠​​‍​‍‌‍‍​​‍​‍​⁠‍‍​‍​‍‌‍⁠‍‌‍⁠​‌⁠‌⁠‌⁠​⁠‌⁠​​​⁠​‍​⁠​‍‌​‌‌​⁠‍‌‌⁠‍‍‌‍⁠⁠‌‍​‍‌⁠‌⁠‌⁠​​‌⁠‍​​⁠‍‌​‍​‍‌⁠⁠‌

More a UI-toolkit decision than pure raster legacy: early window systems (QuickDraw, Windows GDI, X11) pegged the origin to the upper-left to align with text drawing and pointer events, and CSS/AE copied that. If you want ‘cartesian bottom-left’ on the web, wrap your scene and do transform: scaleY(-1), then re-invert only the labels. Any definitive citation, or was it QuickDraw ’84 rather than a chipset?

‌⁠‍⁠​‍​‍‌⁠‌​​‍​‍​⁠‍‍​‍​‍‌‍‌⁠‌‍‌⁠‌‍‌​‌‍‍‍​‍​‍​‍⁠​​‍​‍‌‍‍⁠​‍​‍​⁠‍‍​‍​‍‌⁠​‍‌‍‌‌‌⁠​​‌‍⁠​‌⁠‍‌​‍​‍​‍⁠​​‍​‍‌‍‍‌‌‍‌​​‍​‍​⁠‍‍​⁠‍​​⁠‌‍​⁠‌⁠​⁠​‍​‍⁠​​‍​‍‌‍‌​​‍​‍​⁠‍‍​‍​‍​⁠​‍​⁠​​​⁠​‍​⁠‌‍​⁠​​​⁠​‌​⁠​‌​⁠​‍​‍​‍​‍⁠​​‍​‍‌‍‍​​‍​‍​⁠‍‍​‍​‍‌⁠‌‌​⁠​⁠‌​‍‍‌​⁠⁠‌​‌‌‌‌​⁠​⁠‌‍‌‍‌‌‌⁠‍‌‌​‌‍‌‍‍‌‌‍⁠‌‌⁠‍‌‌‍‍​‌⁠‌‍‌‍‌‌​‍​‍‌⁠⁠‌

AE tip: parent to a null scaled Y:-100, so ‘0–0’ feels bottom-left… PNG rows are top-first: Portable Network Graphics (PNG) Specification (Third Edition).

‌⁠‍⁠​‍​‍‌⁠‌​​‍​‍​⁠‍‍​‍​‍‌‍‌⁠‌‍‌⁠‌‍‌​‌‍‍‍​‍​‍​‍⁠​​‍​‍‌‍‍⁠​‍​‍​⁠‍‍​‍​‍‌⁠​‍‌‍‌‌‌⁠​​‌‍⁠​‌⁠‍‌​‍​‍​‍⁠​​‍​‍‌‍‍‌‌‍‌​​‍​‍​⁠‍‍​⁠‍​​⁠‌‍​⁠‌⁠​⁠​‍​‍⁠​​‍​‍‌‍‌​​‍​‍​⁠‍‍​‍​‍​⁠​‍​⁠​​​⁠​‍​⁠‌‍​⁠​​​⁠​‌​⁠​‌​⁠‌‌​‍​‍​‍⁠​​‍​‍‌‍‍​​‍​‍​⁠‍‍​‍​‍​⁠​⁠‌⁠‍‍‌‍​‍‌​‍‌‌​​‍​⁠‍​​‍⁠‌‌​‌‌‌⁠​​‌‌‍​‌‌‍‌‌‍‌‌‌‌‌‍‌​​‍‌‍‍‍‌​‌⁠​‍​‍‌⁠⁠‌

Early framebuffers scanned from address 0 across the first row, so hardware/DMA made upper-left the natural origin, and VGA → X11/GDI rode that default. On the web it’s mandated: Canvas 2D defines pixel coords starting at the upper-left (HTML Standard). Fun contrast is PostScript/PDF’s lower-left; if you juggle both, add a tiny flip at I/O and keep all your math in one convention.

‌⁠‍⁠​‍​‍‌⁠‌​​‍​‍​⁠‍‍​‍​‍‌‍‌⁠‌‍‌⁠‌‍‌​‌‍‍‍​‍​‍​‍⁠​​‍​‍‌‍‍⁠​‍​‍​⁠‍‍​‍​‍‌⁠​‍‌‍‌‌‌⁠​​‌‍⁠​‌⁠‍‌​‍​‍​‍⁠​​‍​‍‌‍‍‌‌‍‌​​‍​‍​⁠‍‍​⁠‍​​⁠‌‍​⁠‌⁠​⁠​‍​‍⁠​​‍​‍‌‍‌​​‍​‍​⁠‍‍​‍​‍​⁠​‍​⁠​​​⁠​‍​⁠‌‍​⁠​​​⁠​‌​⁠​‌​⁠‌‍​‍​‍​‍⁠​​‍​‍‌‍‍​​‍​‍​⁠‍‍​‍​‍‌‍‍⁠‌‍⁠‌‌​⁠‌​⁠‍​​⁠‌‌‌‍⁠‍‌‌​‍‌‌⁠⁠​⁠​⁠‌‍‌​‌​‌​‌‍‌​​⁠‌‌​⁠​​‌⁠​⁠‌‌‌‍​‍​‍‌⁠⁠‌

Small hack: in SVG, wrap content in a and transform=‘scale(1,-1)’; re-flip text. @OP?

‌⁠‍⁠​‍​‍‌⁠‌​​‍​‍​⁠‍‍​‍​‍‌‍‌⁠‌‍‌⁠‌‍‌​‌‍‍‍​‍​‍​‍⁠​​‍​‍‌‍‍⁠​‍​‍​⁠‍‍​‍​‍‌⁠​‍‌‍‌‌‌⁠​​‌‍⁠​‌⁠‍‌​‍​‍​‍⁠​​‍​‍‌‍‍‌‌‍‌​​‍​‍​⁠‍‍​⁠‍​​⁠‌‍​⁠‌⁠​⁠​‍​‍⁠​​‍​‍‌‍‌​​‍​‍​⁠‍‍​‍​‍​⁠​‍​⁠​​​⁠​‍​⁠‌‍​⁠​​​⁠​‌​⁠​‌​⁠‍​​‍​‍​‍⁠​​‍​‍‌‍‍​​‍​‍​⁠‍‍​‍​‍​⁠‌‌‌​​‌‌‍​⁠‌‌​‍‌​‌‌‌‍⁠‌‌⁠​​‌​⁠​‌‍‍​‌‍‍‌‌​⁠‌​⁠‍​‌​‍‌‌⁠​‍​⁠​‍​⁠​​​‍​‍‌⁠⁠‌