<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Xuepoo Foter</title>
    <description>The latest articles on DEV Community by Xuepoo Foter (@xuepoo).</description>
    <link>https://dev.to/xuepoo</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4022188%2F41678ff7-847d-46a7-9581-4eff2175d347.png</url>
      <title>DEV Community: Xuepoo Foter</title>
      <link>https://dev.to/xuepoo</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/xuepoo"/>
    <language>en</language>
    <item>
      <title>Rethinking Web UIs: A Canvas-Native Retained-Mode UI Runtime</title>
      <dc:creator>Xuepoo Foter</dc:creator>
      <pubDate>Thu, 09 Jul 2026 07:29:32 +0000</pubDate>
      <link>https://dev.to/xuepoo/rethinking-web-uis-a-canvas-native-retained-mode-ui-runtime-285f</link>
      <guid>https://dev.to/xuepoo/rethinking-web-uis-a-canvas-native-retained-mode-ui-runtime-285f</guid>
      <description>&lt;p&gt;The browser DOM is a masterpiece of software engineering. For the last three decades, it has served as the bedrock of the internet, excelling at flowing text, document-first layouts, SEO-heavy prose, and standard form interfaces.&lt;/p&gt;

&lt;p&gt;But the DOM has a fundamental bottleneck: it is designed around a &lt;strong&gt;document flow model&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;When you build applications that behave more like interactive scenes than static documents—such as complex mathematical simulations, node-based diagrams, streaming data dashboards, 3D WebGL scenes, or WebXR interfaces—the "one DOM element per visual item" model breaks down. &lt;/p&gt;

&lt;p&gt;At thousands of entities, the styling, layout computation, and paint operations of the browser’s render pipeline become a massive bottleneck. The traditional workaround has been to drop down into a &lt;code&gt;&amp;lt;canvas&amp;gt;&lt;/code&gt; element. However, doing so immediately strips away the most crucial layers of the modern web stack: layout helper systems, event propagation, accessibility, and automation.&lt;/p&gt;

&lt;p&gt;That is why we built &lt;strong&gt;VectoJS&lt;/strong&gt; (available at &lt;a href="https://vectojs.org" rel="noopener noreferrer"&gt;vectojs.org&lt;/a&gt;) — a zero-dependency, canvas-native UI runtime driven by a &lt;strong&gt;Virtual Math Tree&lt;/strong&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  What is VectoJS?
&lt;/h2&gt;

&lt;p&gt;VectoJS is a retained-mode UI runtime. Instead of placing styled HTML elements in the browser DOM, you construct a hierarchical JavaScript scene graph called the &lt;strong&gt;Virtual Math Tree (VMT)&lt;/strong&gt;. VectoJS manages the layout, computes coordinate transforms, handles pointer events, and paints the final result to high-performance, canvas-backed rendering layers.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;       [ Application State Updates ]
                    │
                    ▼
       ┌───────────────────────────┐
       │   Virtual Math Tree (VMT) │  &amp;lt;── Hierarchical Entities &amp;amp; Layouts
       └────────────┬──────────────┘
                    │
         ┌──────────┴──────────┐
         ▼                     ▼
┌─────────────────┐   ┌─────────────────┐
│ Canvas/GPU Paint│   │  Semantic DOM   │  &amp;lt;── Assistive Tech, Screen Readers,
│  (WebGL/WebGPU) │   │   Projection    │      and AI Coding Agents
└─────────────────┘   └─────────────────┘
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;By bypassing the browser DOM's layout engine, VectoJS gives you pixel-perfect rendering control and high-performance operations, without requiring you to write custom layout math or hit-testing algorithms from scratch.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Core Pillars of VectoJS
&lt;/h2&gt;

&lt;p&gt;We designed VectoJS to solve the three historic problems of canvas-rendered interfaces: accessibility, AI automation, and layout complexity.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Semantic DOM Projection
&lt;/h3&gt;

&lt;p&gt;The biggest critique of canvas-rendered sites is that they are completely inaccessible to screen readers and assistive technology. &lt;/p&gt;

&lt;p&gt;VectoJS solves this with &lt;strong&gt;Semantic DOM Projection&lt;/strong&gt;. While the visible UI is painted to the canvas, VectoJS projects real, invisible semantic HTML nodes (&lt;code&gt;&amp;lt;button&amp;gt;&lt;/code&gt;, &lt;code&gt;&amp;lt;input&amp;gt;&lt;/code&gt;, &lt;code&gt;&amp;lt;a&amp;gt;&lt;/code&gt;, &lt;code&gt;&amp;lt;div role="dialog"&amp;gt;&lt;/code&gt;, etc.) over the corresponding coordinates on the canvas. &lt;/p&gt;

&lt;p&gt;When a screen reader navigates the page, it reads the projected DOM tree. When a user tabs to a button and presses Enter, the browser triggers the event on the projected node, which VectoJS captures and routes back to the canvas entity. This keeps your canvas controls accessible, supports native browser autofill and soft keyboards, and complies with modern無障碍 (accessibility) standards.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Agent-Friendly Architecture
&lt;/h3&gt;

&lt;p&gt;We are entering the era of AI-driven software engineering. But to an AI Coding Agent (like Gemini or Claude), a traditional WebGL canvas is a black box. An agent cannot read the rendering pixels to understand where a button is or if a layout is broken.&lt;/p&gt;

&lt;p&gt;VectoJS exposes a structured, queryable scene graph. Because the semantic projection maps canvas coordinates to real, accessible HTML elements, AI agents can read the DOM projection, understand the interface structure, trigger clicks, input text, and programmatically verify UI states. VectoJS was built from day one to be pair-programmed by AI.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Unified GPU and UI Threading
&lt;/h3&gt;

&lt;p&gt;If you are building a Three.js scene, a custom shader, or a high-velocity particle simulation, you often need interactive controls directly embedded inside the rendering loop. &lt;/p&gt;

&lt;p&gt;VectoJS provides adapters (like &lt;code&gt;@vectojs/three&lt;/code&gt;) that let you project a VectoJS scene graph directly onto a 3D texture, routing 3D raycast pointer inputs back to 2D UI events seamlessly. You can stack standard buttons, dropdowns, and markdown text elements directly inside WebGL and WebXR viewports without introducing layout lag.&lt;/p&gt;




&lt;h2&gt;
  
  
  How VectoJS Differs from Typical Canvas Libraries
&lt;/h2&gt;

&lt;p&gt;Most canvas libraries provide low-level drawing primitives (like rectangles, circles, and path APIs) and leave high-level concepts to the application developer. VectoJS provides a complete runtime stack.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Feature Layer&lt;/th&gt;
&lt;th&gt;VectoJS&lt;/th&gt;
&lt;th&gt;Typical Canvas Libraries (PixiJS, EaselJS, etc.)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Layout System&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Built-in hierarchical stacks, grids, lists, and cards&lt;/td&gt;
&lt;td&gt;Manual coordinate calculation&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Hit-Testing&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Automatic transform conversion and shape testing&lt;/td&gt;
&lt;td&gt;Manual bounding-box or distance calculations&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Event Routing&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Full capture and bubbling phases (similar to DOM)&lt;/td&gt;
&lt;td&gt;Callback-only or flat hit-testing&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Accessibility&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Built-in Semantic DOM Projection&lt;/td&gt;
&lt;td&gt;Usually absent; requires manual overlay syncing&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Text Handling&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Wrapping, BiDi, Arabic support, and MSDF font paths&lt;/td&gt;
&lt;td&gt;Simple single-line &lt;code&gt;fillText&lt;/code&gt; only&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Components&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Buttons, Markdown, Toggle, Scroll, Tables, Dropdowns&lt;/td&gt;
&lt;td&gt;Custom-built by the application&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Video Export&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Deterministic fixed-step frame-by-frame recorder&lt;/td&gt;
&lt;td&gt;External canvas recording hacks&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  Getting Started: Hello World in 20 Lines
&lt;/h2&gt;

&lt;p&gt;VectoJS is built to be modern and lightweight. It runs on Bun, supports npm, and compiles cleanly with TypeScript.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Install the Core Packages
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;bun add @vectojs/core @vectojs/ui
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2. Build Your First Scene
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Scene&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Stack&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@vectojs/core&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Button&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Text&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@vectojs/ui&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// 1. Initialize the canvas-backed Scene&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;canvas&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;gallery-canvas&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;HTMLCanvasElement&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;scene&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Scene&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;canvas&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;maxFPS&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;60&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="c1"&gt;// 2. Create a responsive vertical stack layout&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;layout&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Stack&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;direction&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;vertical&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;gap&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;16&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;24&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;background&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#1a1a24&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;borderRadius&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;12&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="c1"&gt;// 3. Add UI components to the layout&lt;/span&gt;
&lt;span class="nx"&gt;layout&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;VectoJS Engine&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;font&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;bold 24px sans-serif&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#ffffff&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;}));&lt;/span&gt;
&lt;span class="nx"&gt;layout&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Bypassing the DOM for high-performance visual scenes.&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;font&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;14px sans-serif&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#888899&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;}));&lt;/span&gt;
&lt;span class="nx"&gt;layout&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Button&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Start Simulation&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; 
  &lt;span class="na"&gt;onClick&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Simulation starting...&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="na"&gt;style&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;background&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#5d5dff&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#ffffff&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}));&lt;/span&gt;

&lt;span class="c1"&gt;// 4. Mount the layout to the scene and start the loop&lt;/span&gt;
&lt;span class="nx"&gt;scene&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;layout&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;scene&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;start&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  When to Choose VectoJS
&lt;/h2&gt;

&lt;h3&gt;
  
  
  VectoJS Shines in:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;High-Entity Visuals&lt;/strong&gt;: Interfaces containing thousands of moving items or interactive data points.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Math-Driven Layouts&lt;/strong&gt;: Flowcharts, diagrams, graphing interfaces, and custom physics animations.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;3D &amp;amp; Spatial Web&lt;/strong&gt;: UI overlays embedded directly inside WebGL/WebGPU frames or WebXR environments.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Deterministic Recording&lt;/strong&gt;: Interactive workflows that need to be exported frame-by-frame to high-quality H.264 video.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Discover More &amp;amp; Support Us
&lt;/h2&gt;

&lt;p&gt;VectoJS is fully open-source and ready for exploration. &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;🌐 &lt;strong&gt;Documentation &amp;amp; Guide&lt;/strong&gt;: Check out the official website at &lt;a href="https://vectojs.org" rel="noopener noreferrer"&gt;vectojs.org&lt;/a&gt; for detailed guides on accessibility, performance optimizations, and package structures.&lt;/li&gt;
&lt;li&gt;📦 &lt;strong&gt;GitHub Repository&lt;/strong&gt;: Visit &lt;a href="https://github.com/vectojs/vectojs" rel="noopener noreferrer"&gt;github.com/vectojs/vectojs&lt;/a&gt; to star the project, browse the source code, or contribute to our Core, UI, or Three.js packages.&lt;/li&gt;
&lt;li&gt;🎨 &lt;strong&gt;Interactive Gallery&lt;/strong&gt;: Experience VectoJS Native apps in action at &lt;a href="https://gallery.vectojs.org" rel="noopener noreferrer"&gt;vectojs-gallery&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>webdev</category>
      <category>ai</category>
      <category>opensource</category>
      <category>web3</category>
    </item>
    <item>
      <title>I Threw Out the DOM, Kept Accessibility</title>
      <dc:creator>Xuepoo Foter</dc:creator>
      <pubDate>Thu, 09 Jul 2026 05:48:13 +0000</pubDate>
      <link>https://dev.to/xuepoo/i-threw-out-the-dom-kept-accessibility-1bn0</link>
      <guid>https://dev.to/xuepoo/i-threw-out-the-dom-kept-accessibility-1bn0</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;Canvas-native UI runtime with a Virtual Math Tree, semantic accessibility projection, WebGL/WebGPU backends, and agent-drivable controls. &lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;VectoJS is an open-source TypeScript UI framework that renders an entire application to a single &lt;code&gt;&amp;lt;canvas&amp;gt;&lt;/code&gt; element instead of the DOM.&lt;/strong&gt; Every UI framework of the last decade has made a different bet: the DOM is the substrate, and the framework's job is to manage it faster, or hide it behind a nicer syntax. React manages it with a virtual diff. Svelte compiles away the runtime cost of managing it. Tailwind makes styling it less painful. All of them still assume that a UI, underneath, is a tree of HTML elements.&lt;/p&gt;

&lt;p&gt;VectoJS doesn't make that assumption. It renders everything — text, buttons, scrollable lists, thousands of live data points — to one &lt;code&gt;&amp;lt;canvas&amp;gt;&lt;/code&gt; element. There is no HTML tree underneath. A &lt;code&gt;Button&lt;/code&gt; is a plain TypeScript object with a position, a size, and a &lt;code&gt;render()&lt;/code&gt; method; the whole UI is a retained scene graph of these objects, walked and drawn every frame like a game engine's scene, not laid out like a document.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;DangerButton&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;Button&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;override&lt;/span&gt; &lt;span class="nf"&gt;render&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;IRenderer&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;beginPath&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;roundRect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;width&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;height&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fill&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#dc2626&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;super&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;render&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// draw the label on top&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;No CSS specificity, no cascade, no layout thrashing from reading a style back after writing it — the object's own fields are the only state that exists. Inheritance and composition work exactly like they do in any other TypeScript codebase, because this &lt;em&gt;is&lt;/em&gt; just TypeScript, not a templating language pretending to be one.&lt;/p&gt;

&lt;h2&gt;
  
  
  The obvious objection
&lt;/h2&gt;

&lt;p&gt;If you've gotten this far, you're probably thinking: fine, but you've just reinvented &lt;code&gt;&amp;lt;canvas&amp;gt;&lt;/code&gt; games, and canvas is an accessibility black hole. Screen readers see a single opaque bitmap. &lt;code&gt;Ctrl+F&lt;/code&gt; finds nothing. That's true of every canvas app that came before this one, and it's the actual reason most teams don't consider canvas for real UI.&lt;/p&gt;

&lt;p&gt;Here's the mechanism we built to close that gap, concretely: every entity that's &lt;code&gt;interactive&lt;/code&gt; projects a single, invisible, real DOM node — a &lt;code&gt;&amp;lt;button&amp;gt;&lt;/code&gt;, an &lt;code&gt;&amp;lt;input&amp;gt;&lt;/code&gt;, an &lt;code&gt;&amp;lt;a&amp;gt;&lt;/code&gt; — into a layer positioned exactly over the canvas. That node's transform is re-synced every frame, so if the entity moves, scales, or rotates, the invisible DOM node moves with it. Keyboard focus, native pointer events, and screen readers all land on that real element, because it &lt;em&gt;is&lt;/em&gt; one — just transparent, sitting precisely on top of what's drawn.&lt;/p&gt;

&lt;p&gt;This is not free, and we're not going to pretend it is: it's a real DOM node per interactive entity, kept in sync every frame. It's the right cost for buttons, links, and form controls — the things a person actually needs to reach — and it's exactly why we don't set it on the other 99% of a typical VectoJS scene: individual particles, glyphs, and data points cost nothing but a draw call, because nothing about them is interactive.&lt;/p&gt;

&lt;h2&gt;
  
  
  What that split buys you
&lt;/h2&gt;

&lt;p&gt;Because the non-interactive path is free, the workloads VectoJS is actually built for are the ones that make a DOM-based UI fall over: a full node-graph editor with hundreds of draggable, connected nodes and live bezier curves, redrawn at 60fps with zero DOM elements for the graph itself; a WebGPU compute pass moving tens of thousands of particles, with a CPU fallback when WebGPU isn't there; a text reader that reflows an entire book around your cursor in real time, which is a layout operation no browser engine is built to do smoothly at that scale.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://gallery.vectojs.org/#/creation/node-editor" rel="noopener noreferrer"&gt;Try the node editor&lt;/a&gt; · &lt;a href="https://vectojs.org/demos/nexus/" rel="noopener noreferrer"&gt;Try the particle field&lt;/a&gt; · &lt;a href="https://gallery.vectojs.org/#/creation/reader" rel="noopener noreferrer"&gt;Try the reflowing reader&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We're not going to hand you a single "N× faster" number, because there isn't one — it depends on how much of your scene is actually interactive, your GPU, and your browser. What we do ship is a live FPS/entity-count readout on the demos themselves, so you can measure your own hardware instead of trusting ours.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try it yourself
&lt;/h2&gt;

&lt;p&gt;Everything here is MIT-licensed and on GitHub: the &lt;a href="https://github.com/vectojs/vectojs" rel="noopener noreferrer"&gt;engine&lt;/a&gt;, the &lt;a href="https://github.com/vectojs/vectojs-website" rel="noopener noreferrer"&gt;docs site&lt;/a&gt; you might be reading this on, and a community &lt;a href="https://gallery.vectojs.org/" rel="noopener noreferrer"&gt;gallery&lt;/a&gt; where anyone can submit their own VectoJS creation as a one-file pull request.&lt;/p&gt;

&lt;h2&gt;
  
  
  Related reading
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://dev.to/reference/faq/"&gt;FAQ&lt;/a&gt; — the honest version of every question above, including the ones glossed over here.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://dev.to/learn/accessibility/"&gt;Accessibility&lt;/a&gt; — the full projection API.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://dev.to/blog/rethinking-frontend/"&gt;Rethinking Frontend: The VectoJS Philosophy&lt;/a&gt; — the broader case against HTML/CSS as an application substrate.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>webdev</category>
      <category>programming</category>
      <category>opensource</category>
      <category>web3</category>
    </item>
  </channel>
</rss>
