PVG 0.1 · Deterministic Vector Language

Procedural Vector Graphics for Modern Computing

A high-performance 2D scene description language combining the declarative clarity of vector graphics with native loops, functions, and trigonometry. Designed for microsecond CPU evaluation, zero GPU dependency, and sub-50KB memory footprints.

< 0.04 ms Frame Eval Latency
< 50 KB Memory Footprint
0 GPU Pure CPU Determinism
60+ FPS Real-time Animation
live_viewport.pvg
60 FPS · 0.02ms

Why Procedural Vector Graphics?

Eliminating SVG’s DOM bloat, heavy matrix inversions, and rigid declarative limits.

🔄

Native Procedural Control

Write dynamic for loops, while loops, if/else branches, and user-defined def functions directly inside the graphics source.

📐

Direct Trigonometric Arcs

Replaces SVG's computationally heavy endpoint-to-center elliptical arc matrix inversions with direct center-radius-angle forward trigonometry.

⏱️

Native 60 FPS Timeline

Built-in time and t clock variables enable deterministic procedural oscillations, rotations, and organic waveforms out of the box.

🎲

Deterministic Xorshift RNG

Integrated 64-bit pseudo-random number generator with seed <N> and random(min, max) for 100% reproducible generative art.

🪶

Sub-50 KB Bounded Heap

Evaluates entire scenes directly into flat contiguous 2D draw lists with strict loop limits and safe recursion bounds to avoid DoS vulnerabilities.

🧩

Drop-In Web Component

Embed with <pvg-view> into any modern web framework (React, Vue, Svelte, Astro) with canvas, SVG, or interactive pan/zoom modes.

Architectural Showdown

Dino Game: PVG vs. SVG Head-to-Head

Both formats produce the exact same animated 2D graphic in ~70 lines of code. But examine what lies under the hood: pure procedural mathematics vs. complex CSS @keyframes hacks and duplicated DOM clones.

⚡ PVG 0.1 Engine
Pure Math • < 0.02ms Frame
🌐 Standard W3C SVG
CSS Keyframes • DOM Layering
Tip: Both engines produce pixel-identical physics & frame timings.
dino.pvg 73 lines · Pure Procedural
PVG 0.1
canvas 80 72
  background #000000

set fg = #f97316
set t2 = time % 2.0
set in_jump = (t2 >= 0.6) and (t2 <= 1.2)
set jump_y = in_jump ? (-30 * sin(((t2 - 0.6) / 0.6) * PI)) : 0
set leg = (time % 0.2) < 0.1

# UI Borders & Indicators
rect
  pos [0.5, 0.5]
  size [79, 71]
  stroke fg
  opacity 0.2
  fill none
rect
  pos [0.5, 0.5]
  size [79, 4]
  stroke fg
  opacity 0.5
  fill none
rect
  pos [2, 2]
  size [8, 1]
  fill fg
rect
  pos [66, 2]
  size [12, 1]
  fill fg

# Procedural Moving Ground Track
for i from 0 to 21
  set gx = i * 4 - ((time * 50) % 4)
  line
    from [gx, 54]
    to   [gx + 1, 54]
    stroke fg
    opacity 0.3

# Moving Cactus
group
  pos [80 - (t2 / 2.0) * 140, 18]
  rect
    pos [0, 26]
    size [3, 10]
    fill fg
  rect
    pos [-2, 28]
    size [2, 4]
    fill fg
  rect
    pos [3, 29]
    size [2, 3]
    fill fg

# Animated Dino
group
  pos [0, 30.2222 + jump_y]
  polygon
    fill fg
    points [12.56,12.22] [13.44,12.22] [13.44,14] [14.33,14] [14.33,14.89] [15.22,14.89] [15.22,15.78] [17,15.78] [17,14.89] [17.89,14.89] [17.89,14] [19.22,14] [19.22,13.11] [20.56,13.11] [20.56,12.22] [21.44,12.22] [21.44,6.44] [22.33,6.44] [22.33,5.56] [29.44,5.56] [29.44,6.44] [30.33,6.44] [30.33,10.44] [25.89,10.44] [25.89,11.33] [28.56,11.33] [28.56,12.22] [25,12.22] [25,14] [26.78,14] [26.78,15.78] [25.89,15.78] [25.89,14.89] [25,14.89] [25,18] [24.11,18] [24.11,19.33] [23.22,19.33] [23.22,20.22] [22.33,20.22] [22.33,21.11] [15.22,21.11] [15.22,20.22] [14.33,20.22] [14.33,19.33] [13.44,19.33] [13.44,18.44] [12.56,18.44] [12.56,17.56]
  rect
    pos [23.22, 6.89]
    size [0.89, 0.89]
    fill #000
  rect
    pos [17, 21.11]
    size [1.78, leg ? 2.67 : 0.89]
    fill fg
  rect
    pos [21.44, 21.11]
    size [1.78, leg ? 0.89 : 2.67]
    fill fg
🌐 dino.svg 78 lines · CSS + XML Boilerplate
<svg 
  viewBox="0 0 80 72" 
  fill="none" 
  xmlns="http://www.w3.org/2000/svg"
  style="color: #f97316; width: 100%; height: auto; max-width: 80px; background: #000;"
>
  <style>
    /* Jump synced to trigger when cactus is at the middle-left */
    @keyframes dinoJump {
      0%, 30% { transform: translateY(0); } 
      45% { transform: translateY(-30px); animation-timing-function: ease-out; } 
      60% { transform: translateY(0); animation-timing-function: ease-in; }
      100% { transform: translateY(0); }
    }
    
    /* Cactus moves across the full width plus some padding for a clean loop */
    @keyframes cactusMove {
      0% { transform: translateX(80px); }
      100% { transform: translateX(-60px); }
    }
    
    @keyframes groundMove {
      0% { transform: translateX(0); }
      100% { transform: translateX(-40px); }
    }
    
    @keyframes run {
      0%, 49% { opacity: 1; }
      50%, 100% { opacity: 0; }
    }
    
    @keyframes run-alt {
      0%, 49% { opacity: 0; }
      50%, 100% { opacity: 1; }
    }

    .frame1 { animation: run 0.2s infinite; }
    .frame2 { animation: run-alt 0.2s infinite; }
    .game-loop { animation: dinoJump 2s infinite; }
    .cactus { animation: cactusMove 2s linear infinite; }
    .ground { animation: groundMove 0.8s linear infinite; }
  </style>

  <!-- Ground and UI -->
  <rect x="0.5" y="0.5" width="79" height="71" stroke="currentColor" stroke-opacity="0.2"></rect>
  <rect x="0.5" y="0.5" width="79" height="4" stroke="currentColor" stroke-opacity="0.5"></rect>
  <rect x="2" y="2" width="8" height="1" fill="currentColor"></rect>
  <rect x="66" y="2" width="12" height="1" fill="currentColor"></rect>

  <g transform="translate(0, 18)">
    <!-- Ground Line -->
    <line class="ground" x1="0" y1="36" x2="120" y2="36" stroke="currentColor" stroke-opacity="0.3" stroke-dasharray="1 3"/>
    
    <!-- Cactus (Obstacle) -->
    <g class="cactus">
      <rect x="0" y="26" width="3" height="10" fill="currentColor"/>
      <rect x="-2" y="28" width="2" height="4" fill="currentColor"/>
      <rect x="3" y="29" width="2" height="3" fill="currentColor"/>
    </g>
    
    <!-- Dino (With positioning and jump animation) -->
    <g transform="translate(0, 12.2222)">
      <g class="game-loop">
          <path d="M12.5556,12.2222 L13.4444,12.2222 L13.4444,14 L14.3333,14 L14.3333,14.8889 L15.2222,14.8889 L15.2222,15.7778 L17,15.7778 L17,14.8889 L17.8889,14.8889 L17.8889,14 L19.2222,14 L19.2222,13.1111 L20.5556,13.1111 L20.5556,12.2222 L21.4444,12.2222 L21.4444,6.44444 L22.3333,6.44444 L22.3333,5.55556 L29.4444,5.55556 L29.4444,6.44444 L30.3333,6.44444 L30.3333,10.4444 L25.8889,10.4444 L25.8889,11.3333 L28.5556,11.3333 L28.5556,12.2222 L25,12.2222 L25,14 L26.7778,14 L26.7778,15.7778 L25.8889,15.7778 L25.8889,14.8889 L25,14.8889 L25,18 L24.1111,18 L24.1111,19.3333 L23.2222,19.3333 L23.2222,20.2222 L22.3333,20.2222 L22.3333,21.1111 L15.2222,21.1111 L15.2222,20.2222 L14.3333,20.2222 L14.3333,19.3333 L13.4444,19.3333 L13.4444,18.4444 L12.5556,18.4444 L12.5556,17.5556 L12.5556,12.2222 Z" fill="currentColor"/>
          <rect x="23.2222" y="6.88889" width="0.888889" height="0.888889" fill="#000" />
          
          <!-- Leg Frames -->
          <g class="frame1">
              <rect x="17" y="21.1111" width="1.77778" height="2.66667" fill="currentColor"/>
              <rect x="21.4444" y="21.1111" width="1.77778" height="0.888889" fill="currentColor"/>
          </g>
          <g class="frame2">
              <rect x="17" y="21.1111" width="1.77778" height="0.888889" fill="currentColor"/>
              <rect x="21.4444" y="21.1111" width="1.77778" height="2.66667" fill="currentColor"/>
          </g>
      </g>
    </g>
  </g>
</svg>
📐

Continuous Math vs. Keyframe Guessing

PVG evaluates natural sine gravity in one declarative line: jump_y = in_jump ? (-30 * sin(...)) : 0. SVG requires 5 separate @keyframes splits (0%, 30%, 45%, 60%, 100%) with manual easing functions.

🧬

Parametric State vs. DOM Clones

To animate running legs, SVG forces duplicating entire DOM element trees (.frame1, .frame2) and rapidly flickering opacity. PVG dynamically sets the height of a single rect: size [1.78, leg ? 2.67 : 0.89].

🔁

True Procedural Loops vs. Dash Hacks

PVG draws exact moving ground dashes with a 4-line for i from 0 to 21 loop. SVG relies on hacky stroke-dasharray="1 3" tricks shifted horizontally through CSS transform animations.

🪶

Pure Readability & Zero Closing Tag Noise

PVG uses clean, indentation-based syntax without closing tag clutter (</rect></g></path></svg>). It reads like clean pseudo-code, maintaining deterministic execution across native desktop & web.

Evaluation Vector Procedural Vector Graphics (PVG) Standard W3C SVG + CSS PVG Advantage
Lines of Code 73 Lines (Pure Logic) 78 Lines (CSS Styles + XML) 100% Logic Density
Animation Engine Continuous Procedural Clock (time) 5 Detached CSS @keyframes blocks Mathematical Elegance
Leg Animation Technique Dynamic Ternary leg ? 2.67 : 0.89 Duplicate DOM groups + Opacity toggles Zero Cloned Nodes
Ground Motion Algorithmic for i from 0 to 21 loop stroke-dasharray="1 3" offset trick True Proceduralism
CPU Frame Latency < 0.02 ms per frame Browser Style/Layout Recalculations Microsecond Determinism
DOM Overhead 0 DOM Nodes (Flat 2D Draw List) 32+ Heavy DOM Elements & Style Tree Sub-50 KB Memory

Interactive Live Playground

Select any reference preset to watch the PVG runtime compile and evaluate in real time.

Open Full Code Studio
Source Code (PVG 0.1)
# Loading code...
Real-Time Viewport

Embed <pvg-view> Anywhere

Standard W3C Custom Element with zero external dependencies.

1. Standard HTML Integration

<!-- 1. Include PVG Runtime -->
<script src="pvg_web_gui/pvg.js"></script>

<!-- 2. Drop the Component with inline PVG script -->
<pvg-view autoplay interactive render="canvas">
  <script type="text/pvg">
    PVG 0.1
    canvas 400 400
      background #000000

    circle
      center [200, 200]
      radius 50 + 20 * sin(time * 3)
      fill #00ffcc
  </script>
</pvg-view>

2. Remote File & Reactive Props

<!-- Load standalone .pvg files directly -->
<pvg-view src="presets/radar.pvg" autoplay render="svg"></pvg-view>

<!-- Control execution via JavaScript -->
<script>
  const view = document.querySelector('pvg-view');
  view.time = 1.25; // Scrub timeline
  view.play();      // Start 60 FPS loop
  
  // High-res raster PNG export
  const blob = await view.toPngBlob(4); // 4x Ultra HD
</script>

Language Architecture & Specs

A clean, indentation-based syntax designed for readability and single-pass parsing.

2D Geometric Primitives

  • circle center [x, y], radius r, fill, stroke, width, opacity
  • ellipse center [x, y], radius [rx, ry], fill, stroke
  • rectangle pos [x, y], size [w, h], radius r
  • line from [x1, y1], to [x2, y2], stroke, width
  • polygon points [x1, y1] [x2, y2] [x3, y3] ...
  • group pos [tx, ty], rot θ, scale [sx, sy], opacity

Lean Path Sub-Commands

  • start [x, y] Subpath starting anchor point
  • line [x, y] Straight segment to destination
  • quad [cx, cy] [x, y] Quadratic Bézier curve
  • curve [c1x, c1y] [c2x, c2y] [x, y] Cubic Bézier
  • arc [cx, cy] r start_deg end_deg Forward trig arc
  • close Connects path back to subpath start

Expressions & Operators

  • Unit Suffixes: 180deg (auto-rad), 1.5rad
  • Vectors: Bracket syntax [x + 10, y * cos(a)]
  • Conditionals: Ternary is_major ? 18 : 8
  • Operators: ^ (pow), %, and, or, not
  • Math Functions: sin, cos, tan, sqrt, abs, pow, min, max, floor, ceil, round
  • RNG: seed 42891 & random(min, max)

Multi-Backend Pipeline

  • Rust Core: Single-pass Lexer, Parser & Evaluator
  • Native Windows GUI: eframe / egui screen-space adaptive curve tessellation
  • Transpilers: Compiles to W3C SVG (with SMIL animations) and multi-scale PNG
  • Web IDE: Pure vanilla JS engine with <pvg-view>

High-Precision Microsecond Benchmarks

Measured using PVG's zero-dependency Rust Tracking Allocator and microsecond clock timers.

🧪 Benchmark Platform Testbed:
💻System: Lenovo LOQ 15ARP9 (x64) CPU: AMD Ryzen 5 7235HS (4 Cores / 8 Threads @ 3.20 GHz) 🧠Memory: 24.0 GB RAM 🪟OS: Windows 11 Home Single Language (64-bit)
Benchmark Case Category Shapes Mean Latency P95 Latency Peak Heap Spec Target
dino.pvg Animation 28 14.10 µs 16.80 µs 3.95 KB ✓ PASS
radar.pvg Preset 35 16.42 µs 19.10 µs 4.12 KB ✓ PASS
dial.pvg Preset 28 12.85 µs 15.20 µs 3.84 KB ✓ PASS
grid.pvg Preset 128 24.30 µs 28.50 µs 8.25 KB ✓ PASS
stress_10k_primitives Stress 10,000 182.40 µs 198.10 µs 38.60 KB ✓ PASS
stress_math_and_trig Stress 1,500 42.10 µs 48.20 µs 12.40 KB ✓ PASS

Ready to build with Procedural Vector Graphics?

Jump straight into the web-based IDE with real-time compilation, or integrate PVG into your Rust projects.