웹 디자인 파이프라인 스킬과 이를 5개 에이전트에 설치하는 CLI 를 담은 모노레포. 스킬 (packages/skill) - SKILL.md 261줄 + 참조 문서 16개 3,349줄. progressive disclosure 로 본문은 절차와 인덱스만, 지식은 references/ 로 분리 - 0~6단계 파이프라인. 규모에 따라 전체·연장·국소 세 경로로 분기 - 하드 게이트 12개는 grep·카운트로 검증 가능한 것만. 취향 판단은 제외 - 미학 프리셋 5종, AI 슬롭 지문 목록, 한글 조판 규칙, SVG 필터·three.js·인터랙티브 모션·HTML-in-Canvas 실전 지침 설치 CLI (packages/cli, packages/core) - npx designpaca 온보딩 TUI. Claude Code · Codex · Cursor · Windsurf · AGENTS.md - 매니페스트에 설치 시점 해시를 기록해 사용자가 고친 파일은 update 가 건너뛴다 - 타깃별로 본문의 references/ 경로를 실제 설치 위치로 재작성 - AGENTS.md 는 항상 로드되므로 본문 대신 303자 포인터만 주입 - Windsurf 는 12,000자 상한 초과 시 설치를 차단 배포 (build/ci, .forgejo/workflows) - 태그 v* → 검사·테스트·빌드 → npmjs 배포 + Forgejo 레지스트리 미러 → draft 릴리스 → Cloudflare Pages. 재실행 멱등 근거 (research/) - 약 250개 웹 소스 조사 결과와 도그푸딩 검증 2건. 스킬의 모든 수치는 여기서 나온다 테스트 22개 통과 (core 16 · cli 6)
23 KiB
01. HTML-in-Canvas API 정확한 스펙
조사 기준일: 2026-08-20 1차 출처: WICG 공식 explainer(living document), WHATWG HTML PR, Chrome Platform Status, Chrome for Developers 블로그, blink-dev Intent 스레드 이 문서의 모든 시그니처는 WICG explainer의 IDL 블록 원문에서 그대로 옮긴 것이다. 추측한 부분은 명시적으로 "미확인"으로 표시했다.
0. 한 줄 요약
<canvas layoutsubtree> 안에 실제 HTML을 넣고, paint 이벤트 안에서 ctx.drawElementImage(el, x, y)(2D) / gl.texElementImage2D(...)(WebGL) / device.queue.copyElementImageToTexture(...)(WebGPU)를 호출하면, 그 HTML의 살아있는 렌더링 결과가 캔버스 픽셀(또는 GPU 텍스처)로 들어온다. 원본 요소는 DOM에 그대로 남아 클릭·포커스·접근성·find-in-page가 계속 동작한다.
1. 공식 명칭과 저장소
| 항목 | 값 |
|---|---|
| 기능 이름 | HTML-in-canvas (Chrome Platform Status 등록명) |
| 인큐베이션 | W3C WICG |
| Explainer 저장소 | https://github.com/WICG/html-in-canvas |
| Explainer 렌더링 | https://wicg.github.io/html-in-canvas/ (형식 스펙이 아니라 explainer 그 자체) |
| 스펙 PR | https://github.com/whatwg/html/pull/11588 — "Add HTML-in-Canvas APIs", 2025-08-21 개설, 2026-08 현재 open / 미머지 |
| chromestatus | https://chromestatus.com/feature/5172548013916160 |
| Chromium 버그 | https://crbug.com/500967896 (Blink 컴포넌트: Blink>Canvas) |
| 저자 | Philip Rogers, Stephen Chenney(Igalia), Chris Harrelson, Philip Jägenstedt, Khushal Sagar, Vladimir Levin, Fernando Serboncini |
1.1 이름 변천사 — 영상/구 자료의 이름이 지금과 다른 이유
노마드코더 영상 및 상당수의 블로그가 쓰는 canvas place element / drawElement / setHitTestRegions()는 모두 옛 이름이다. WICG 저장소 커밋 로그로 확인한 실제 변천:
| 날짜 | 변경 |
|---|---|
| ~2025-08 이전 | 제안 이름 canvas place element, 메서드 drawElement(), 히트테스트는 setHitTestRegions() |
| 2025-08-22 | 메서드 rename (drawElement → drawHTMLElement) |
| 2025-09-05 | drawHTMLElement → drawHTML |
| 2025-09-11 | drawHTML → drawElementImage ← 현재 이름 |
| 2025-10-08 | explainer에서 "place element" 표현 전부 제거 (저장소도 WICG/canvas-place-element → WICG/html-in-canvas. 옛 저장소는 현재 404) |
| 2025-11-08 | setHitTestRegions() 폐기 → "Switch to CSS transforms for hit testing" (반환된 DOMMatrix를 element.style.transform에 넣는 방식으로 대체) |
| 2026-02-10~25 | paint 이벤트 / onpaint 설계 확정, 이벤트에서 time 인자 제거 |
| 2026-03-17~31 | captureElementImage() + ElementImage (OffscreenCanvas 지원) 추가 |
| 2026-03-20 | drawElementImage() 소스 사각형(sx/sy/swidth/sheight) 오버로드 추가 |
| 2026-04-16 / 06-01 | WebGL/WebGPU IDL 대폭 변경 (아래 4·5절의 "구 시그니처" 주의사항 참조) |
| 2026-06-16 | "privacy-preserving painting" → "read-back-allowed rendering" 으로 개념 이름 변경 |
| 2026-07-13/14 | 중첩 canvas 허용, paint 이벤트가 역트리 순서로 발화함을 명시 |
⚠️ WHATWG 스펙 PR #11588 본문에는 아직
setHitTestRegions()와drawable속성이 남아 있다. explainer(=구현 기준)와 스펙 PR이 아직 동기화되지 않은 상태이므로, 구현 기준은 explainer를 따라야 한다.
2. layoutsubtree 속성
철자: 전부 소문자 layoutsubtree (HTML 속성). IDL 반사 프로퍼티는 camelCase layoutSubtree.
<canvas id="canvas" style="width:400px; height:200px" layoutsubtree>
<form id="form_element">
<label for="name">name:</label>
<input id="name">
</form>
</canvas>
boolean타입 → 불리언 속성. 존재하기만 하면 true다.layoutsubtree="true"도 되고(WICG 예제가 이 형태를 쓴다)layoutsubtree=""도 된다.layoutsubtree="false"라고 써도 true로 취급되니 끄려면 속성 자체를 제거해야 한다.- 효과 (explainer 원문 기준):
- 캔버스 자손이 레이아웃에 참여하고 히트테스트에 참여한다.
<canvas>의 직계 자식은 stacking context를 생성하고, 모든 자손의 containing block이 되며, paint containment를 갖는다.- 자식은 "보이는 것처럼" 동작하지만,
drawElementImage()로 명시적으로 그려지기 전까지 사용자에게는 보이지 않는다.
- 이 속성이 없으면 캔버스 자식은 종전대로 fallback 콘텐츠일 뿐이고,
drawElementImage()는 예외를 던진다.
3. 2D 컨텍스트 — drawElementImage()
3.1 IDL (explainer 원문 그대로)
interface mixin CanvasDrawElementImage {
DOMMatrix drawElementImage((Element or ElementImage) element,
unrestricted double dx, unrestricted double dy);
DOMMatrix drawElementImage((Element or ElementImage) element,
unrestricted double dx, unrestricted double dy,
unrestricted double dwidth, unrestricted double dheight);
DOMMatrix drawElementImage((Element or ElementImage) element,
unrestricted double sx, unrestricted double sy,
unrestricted double swidth, unrestricted double sheight,
unrestricted double dx, unrestricted double dy);
DOMMatrix drawElementImage((Element or ElementImage) element,
unrestricted double sx, unrestricted double sy,
unrestricted double swidth, unrestricted double sheight,
unrestricted double dx, unrestricted double dy,
unrestricted double dwidth, unrestricted double dheight);
};
CanvasRenderingContext2D includes CanvasDrawElementImage;
OffscreenCanvasRenderingContext2D includes CanvasDrawElementImage;
오버로드는 인자 개수 3 / 5 / 7 / 9개 네 가지다 — (el, dx, dy), (el, dx, dy, dw, dh), (el, sx, sy, sw, sh, dx, dy), (el, sx, sy, sw, sh, dx, dy, dw, dh). 기존 CanvasRenderingContext2D.drawImage()와 정확히 같은 모양이고, 첫 인자만 이미지 소스 대신 Element(또는 ElementImage)로 바뀐 것이다.
3.2 반환값
DOMMatrix — 이 행렬을 element.style.transform = returned.toString() 으로 적용하면, DOM 상의 요소 위치가 캔버스에 그려진 위치와 일치한다. 히트테스트·포커스 링·IntersectionObserver·접근성 좌표가 이 DOM 위치를 쓰기 때문에 이 동기화를 하지 않으면 클릭이 엉뚱한 곳에 떨어진다.
3.3 요구사항·제약 (explainer 원문 항목)
- 최근 렌더링 업데이트 시점에
<canvas>에layoutsubtree가 지정되어 있어야 한다. element는 최근 렌더링 업데이트 시점에<canvas>의 직계 자식(direct child) 이어야 한다.element가 박스를 생성해야 한다 (즉display:none이면 안 된다).- 변환(Transforms): 캔버스의 현재 변환 행렬(CTM)은 그리기에 적용된다. 반면 소스
element에 걸린 CSS transform은 그리기에서 무시된다. (다만 히트테스트/접근성에는 계속 영향을 준다 — 그래서 3.2의 동기화가 성립한다.) - 클리핑: 넘치는 콘텐츠(layout overflow, ink overflow 모두)는 요소의 border box로 클리핑된다.
- 크기:
width/height인자는 캔버스 좌표계의 목적지 사각형이다. 생략하면 캔버스 밖에 있을 때와 같은 화면상 크기·비율이 되도록 자동 사이징된다. → 실전 의미:canvas.width를 device pixel로 잡으면 x/y 좌표도 device pixel 단위여야 한다 (x * devicePixelRatio).
3.4 스냅샷 타이밍 (중요)
- 캔버스 모든 자식의 렌더링 스냅샷은
paint이벤트 직전에 기록된다. paint이벤트 안에서 호출하면 → 현재 프레임의 모습으로 그려진다.paint이벤트 밖에서 호출하면 → 직전 프레임의 스냅샷이 쓰인다.- 최초 스냅샷이 기록되기 전에 호출하면 예외가 발생한다 (Chromium 구현에서는
InvalidStateError).
4. WebGL — texElementImage2D()
4.1 현재 IDL (explainer 원문)
dictionary WebGLCopyElementImageConfig {
GLfloat sx;
GLfloat sy;
GLfloat swidth;
GLfloat sheight;
GLsizei width;
GLsizei height;
};
partial interface WebGLRenderingContext {
void texElementImage2D(GLenum target, GLenum internalformat,
(Element or ElementImage) element,
optional WebGLCopyElementImageConfig config = {});
};
호출:
gl.texElementImage2D(gl.TEXTURE_2D, gl.RGBA8, element);
4.2 ⚠️ 시그니처가 2026-04~06에 바뀌었다
- 구 시그니처:
texElementImage2D(target, level, internalformat, format, type, element)—texImage2D와 똑같은 6인자. - 신 시그니처:
texElementImage2D(target, internalformat, element, config?)—level,format,type이 사라졌다. - Chrome for Developers 블로그(2026-05-19 최종 수정)의 코드 예제는 아직 구 시그니처(
gl.texElementImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, form_element))를 보여준다. 반면 WICG 공식 예제Examples/webGL.html은 try/catch로 신·구 양쪽을 지원한다:
try {
gl.texElementImage2D(gl.TEXTURE_2D, gl.RGBA8, draw_element); // 신
} catch (e) {
gl.texElementImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, // 구
gl.UNSIGNED_BYTE, draw_element);
}
→ 실전 코드에서는 이 try/catch 패턴을 그대로 쓰는 게 안전하다.
4.3 알려진 미해결 이슈
- Jake Archibald가 blink-dev Intent 스레드에서 지적: WebGL 경로에서 텍스처 크기를 지정할 방법이 없다.
config의width/height가 추가된 배경이지만 논의는 진행 중.
5. WebGPU — copyElementImageToTexture()
5.1 현재 IDL (explainer 원문)
dictionary GPUCopyElementImageDestination {
required GPUImageCopyTextureTagged destination;
GPUIntegerCoordinate width;
GPUIntegerCoordinate height;
};
dictionary GPUCopyElementImageSource {
required (Element or ElementImage) source;
float sx;
float sy;
float swidth;
float sheight;
};
partial interface GPUQueue {
void copyElementImageToTexture(GPUCopyElementImageSource source,
GPUCopyElementImageDestination destination);
}
5.2 실제 호출 형태 (WICG 젤리 슬라이더 데모 Examples/webgpu-jelly-slider/src/index.ts 원문)
canvas.onpaint = () => {
const sourceDict = { source: valueElement };
const destDict = {
destination: { texture: valueRawTexture },
width: width,
height: height
};
try {
device.queue.copyElementImageToTexture(sourceDict, destDict); // 신
} catch (e) {
device.queue.copyElementImageToTexture(valueElement, width, height, // 구
{ texture: valueRawTexture });
console.log('Note: using old copyElementImageToTexture API');
}
// ... transform 동기화
};
⚠️ Chrome 블로그는
device.queue.copyElementImageToTexture(valueElement, { texture: targetTexture })라는 또 다른(간략화된/구) 형태를 보여준다. IDL과 WICG 데모 소스가 서로 일치하므로 위 dictionary 2개 형태가 현재 기준이다.
copyExternalImageToTexture()의 DOM 요소 버전이라고 생각하면 된다.
6. paint 이벤트
6.1 IDL
[Exposed=Window]
interface PaintEvent : Event {
constructor(DOMString type, optional PaintEventInit eventInitDict);
readonly attribute FrozenArray<Element> changedElements;
};
dictionary PaintEventInit : EventInit {
sequence<Element> changedElements = [];
};
canvas.onpaint = fn 또는 canvas.addEventListener('paint', fn) 둘 다 가능.
6.2 동작 규칙 (explainer 원문 기준)
- 캔버스 자식들의 렌더링이 변했을 때 발화한다 (포커스, 호버, 입력, CSS 애니메이션 등).
- 발화 시점: update-the-rendering 중 IntersectionObserver 단계가 실행된 직후. 설계 문서상으로는 "Paint 단계 직후, 루프 없이 프레임당 1회"(explainer의 Option C).
- 이벤트는 바뀐 자식들의 목록(
changedElements)을 담는다. - 캔버스 자식의 CSS transform 변경은 렌더링에서 무시되므로, transform만 바꿔서는 다음 프레임에
paint가 발화하지 않는다. (→ 3.2의 동기화 코드가 무한 루프를 만들지 않는 이유) paint안에서 한 캔버스 드로잉 명령은 현재 프레임에 반영되지만,paint안에서 한 DOM 변경은 다음 프레임부터 반영된다.<canvas>가 여러 개면paint는 역트리 순서(reverse tree order) 로 발화한다 → 자손이 조상보다 먼저 발화한다 (중첩 canvas 지원, 2026-07 추가).
6.3 requestPaint()
void requestPaint();
자식이 하나도 안 바뀌어도 paint를 한 번 강제로 발화시킨다. explainer 표현으로 "requestAnimationFrame()과 유사". 매 프레임 갱신이 필요한 앱은 onpaint 핸들러 끝에서 requestPaint()를 다시 호출해 루프를 만든다. 또한 최초 1회 호출해서 렌더 파이프라인을 킥스타트해야 한다(첫 스냅샷 확보).
7. OffscreenCanvas / Worker — captureElementImage() & ElementImage
partial interface HTMLCanvasElement {
[CEReactions, Reflect] attribute boolean layoutSubtree;
attribute EventHandler onpaint;
void requestPaint();
ElementImage captureElementImage(Element element);
DOMMatrix getElementTransform((Element or ElementImage) element, DOMMatrix drawTransform);
};
partial interface OffscreenCanvas {
DOMMatrix getElementTransform((Element or ElementImage) element, DOMMatrix drawTransform);
};
[Exposed=(Window,Worker), Transferable]
interface ElementImage {
readonly attribute double width;
readonly attribute double height;
undefined close();
};
canvas.captureElementImage(element)→ 요소 렌더링의 전송 가능한(Transferable) 스냅샷.postMessage(msg, [elementImage])로 워커에 넘기고, 워커의OffscreenCanvasRenderingContext2D.drawElementImage(elementImage, x, y)로 그린다.- 워커에서 계산된 transform은
postMessage로 메인 스레드에 돌려보내element.style.transform에 적용해야 한다. 위치가 동적이면 메인 스레드에서 미리 계산해ElementImage전송과 동시에 적용하는 편이 낫다.
8. getElementTransform() — 3D 컨텍스트용 동기화 헬퍼
DOMMatrix getElementTransform((Element or ElementImage) element, DOMMatrix drawTransform);
HTMLCanvasElement와OffscreenCanvas에 있다. (Chrome 블로그 본문 산문이element.getElementTransform()이라고 쓴 곳이 있는데, 블로그 자체의 코드 예제와 IDL 모두canvas.getElementTransform(el, matrix)이므로 산문 쪽이 오기다.)- WebGL/WebGPU에서는 요소의 최종 화면 위치를 셰이더가 결정하므로
drawElementImage()처럼 자동으로 알 수 없다. 그래서 개발자가 MVP 행렬을 스크린 스페이스 행렬로 변환해 넘겨주면, 이 메서드가style.transform에 넣을DOMMatrix를 돌려준다.
8.1 변환 공식 (explainer 원문)
T_origin⁻¹ · S_css→grid⁻¹ · T_draw · S_css→grid · T_origin
T_draw: 캔버스 그리드 좌표계에서 요소를 그린 변환.drawElementImage의 경우CTM · T_(x,y) · S_(destScale).T_origin: 요소의 계산된transform-origin평행이동 행렬.S_css→grid: CSS 픽셀 → 캔버스 그리드 픽셀 스케일 행렬.
8.2 WebGL에서 screenSpaceTransform 만드는 절차 (Chrome 블로그 원문 코드)
// 1. WebGL MVP → DOMMatrix
const mvpDOM = new DOMMatrix(Array.from(htmlElementMVP));
// 2. HTML 요소 정규화 (px → 1x1 단위 사각형, Y축 뒤집기)
const width = targetHTMLElement.offsetWidth;
const height = targetHTMLElement.offsetHeight;
const cssToUnitSpace = new DOMMatrix()
.scale(1 / width, -1 / height, 1)
.translate(-width / 2, -height / 2);
// 3. 클립 공간 → 캔버스 뷰포트
const clipToCanvasViewport = new DOMMatrix()
.translate(canvas.width / 2, canvas.height / 2)
.scale(canvas.width / 2, -canvas.height / 2, 1);
// 4. 합성: (Clip→Pixels) * MVP * (px→unit)
const screenSpaceTransform = clipToCanvasViewport.multiply(mvpDOM).multiply(cssToUnitSpace);
// 5. 적용
const computedTransform = canvas.getElementTransform(targetHTMLElement, screenSpaceTransform);
if (computedTransform) targetHTMLElement.style.transform = computedTransform.toString();
9. 접근성·히트테스트가 유지되는 원리
핵심은 "같은 요소가 두 곳에 존재"가 아니라 "요소는 DOM에 한 번만 존재하고, 캔버스에는 그 픽셀 복사본이 있다" 는 것이다.
- 히트테스트, 포커스, 키보드 탭 순서, 텍스트 선택, 복사/붙여넣기, 우클릭 컨텍스트 메뉴, find-in-page, 번역, 리더 모드, 확장 프로그램, 브라우저 줌, 자동완성 — 전부 DOM 쪽이 처리한다. 캔버스는 픽셀만 담당한다.
layoutsubtree가 자식들을 접근성 트리에 노출시킨다. 기존 canvas fallback 콘텐츠와 달리, 그려진 내용과 접근성 트리가 구조적으로 일치함이 보장된다 (explainer가 명시한 주요 동기 중 하나).- 단 DOM 위치와 그려진 위치가 어긋나면 전부 어긋난다. →
drawElementImage()의 반환DOMMatrix(또는getElementTransform())를 매 프레임style.transform에 반영하는 것이 이 API의 필수 계약이다. - DevTools에서 캔버스 안 HTML을 그대로 인스펙트/스타일 수정할 수 있고, 수정 즉시 텍스처에 반영된다.
10. 보안·프라이버시: "Read-back-allowed rendering"
(구 명칭 "privacy-preserving painting", 2026-06-16 개칭)
캔버스 픽셀은 getImageData()로 읽을 수 있고, WebGL/WebGPU에서는 항상 읽을 수 있다. 따라서 저자 코드가 원래 볼 수 없던 정보는 애초에 그려지지 않는다. 페인팅(픽셀 읽기·타이밍 공격)과 무효화(onpaint 발화 여부) 양쪽 모두에서 민감 정보를 배제한다.
그려지지 않는(=민감) 정보
- cross-origin 데이터:
<iframe>·<img>등 embedded content의 교차 출처 콘텐츠,url()참조(background-image,clip-path), 교차 출처로 오염(tainted)된<canvas>, SVG의<use>/<pattern>/<feImage>. → same-origin iframe은 그려진다. 그 안의 cross-origin 콘텐츠만 안 그려진다. - 시스템 색상 / 테마 / 사용자 환경설정
- 맞춤법·문법 검사 밑줄 마커
- 방문한 링크 정보(
:visited) — 히스토리 스니핑 방지 - JS로 접근 불가한 대기 중 폼 자동완성 정보
- 서브픽셀 텍스트 안티에일리어싱
- 캡션/자막 선택 및 외형에 대한 사용자 설정
- IME 팝업 및 IME 고유 텍스트 서식
민감하지 않다고 판정된(=그려지는) 새 정보
- find-in-page 검색어 하이라이트, text-fragment(URL 프래그먼트) 마커
- 스크롤바 및 폼 컨트롤 외형 (Blink/WebKit에서 이미
foreignObject로 탐지 가능) - 캐럿 깜빡임 속도
forced-colors(이미 미디어 쿼리 + 시스템 색상으로 JS에서 알 수 있음)
blink-dev Intent에 명시된 잔여 리스크: "이 API는 그라디언트 픽셀, 폼 컨트롤 렌더링 등 소량의 새 정보를 노출하며 이는 상호운용성 리스크를 만든다." Mozilla의 우려도 주로 이 핑거프린팅 지점이다.
11. 기타 확인된 제약
| 제약 | 내용 | 출처 |
|---|---|---|
| cross-origin iframe | 지원 안 함 (위 10절) | Chrome 블로그 "Limitations" |
| 메인 스레드 스크롤 | 캔버스 내부 콘텐츠는 JS로 그려지므로 스크롤·애니메이션이 JS와 독립적으로 갱신될 수 없다. 컴포지터 스레드 스크롤의 이점을 잃는다. 캔버스 안에 스크롤 콘텐츠를 넣을지, 캔버스 전체를 스크롤시킬지 신중히 판단하라. | Chrome 블로그 "Limitations" |
| 중첩 canvas | explainer는 자손을 조상 캔버스에 그리는 것을 허용하지만, Chromium Canary 구현은 가장 가까운 canvas 조상으로 제한한다 (스펙 PR #11588에서 미해결 논의 중) | whatwg/html#11588 |
| ElementImage 교차 캔버스 | ElementImage를 만든 캔버스 외의 캔버스에 그리는 것을 제한할지 논의 중 (접근성·래스터화 복잡도 vs 오래된 스냅샷 문제) |
whatwg/html#11588 |
| GC 압박 | dictionary 기반 API 설계 때문에 애니메이션 중 잦은 GC가 발생한다는 리뷰 지적 | whatwg/html#11588 |
| 크기/리사이즈 | 커뮤니티 공통 지적: "이 API에서 크기와 리사이즈가 유일하게 덜 익은 부분". <canvas>는 div처럼 width:100%가 기본이 아니고 콘텐츠에 따라 높이가 자라지도 않는다. |
Frontend Masters (Amit Sheen, 2026-04-21) |
| 첫 스냅샷 전 호출 | InvalidStateError throw. 반드시 onpaint 안에서 그리고, requestPaint()로 킥스타트 |
explainer + Matt Rothenberg |
| 캔버스 자식 크기 | 캔버스 자식의 크기가 캔버스 CSS 크기와 불일치하면 텍스처가 늘어나고 좌표가 페이지 아래로 갈수록 누적 오차 | Matt Rothenberg 실전 노트 |
| 반투명 배경 | 반투명 input 배경은 셰이더 효과가 비쳐 나오므로 불투명 색을 쓸 것 | Matt Rothenberg 실전 노트 |
| WebGL Y축 | 텍스처는 top-down, WebGL은 bottom-up → UV에서 Y 뒤집기 필요 | Matt Rothenberg 실전 노트 |
| 전체 화면 후처리 | 페이지 전체에 후처리를 걸면 오히려 접근성 이점을 훼손할 수 있다 | Codrops |
12. 향후 방향 (explainer "Future considerations")
Auto-updating canvas 모드: drawElementImage가 "최신 렌더링을 가리키는 플레이스홀더"를 기록하고, 캔버스가 커맨드 버퍼를 보관해 스크롤/애니메이션 갱신마다 자동 재생하는 모드. 스크립트를 블로킹하지 않고 컴포지터 스레드 스크롤·애니메이션과 완벽히 동기화되는 효과를 가능하게 한다. 2D 컨텍스트에는 실현 가능, WebGPU도 소폭 API 추가로 가능할 것으로 보고 있다. WebGL은 getError() 등 플러시가 필요한 API 때문에 이 모델이 근본적으로 불가하다고 explainer가 명시.