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.
Eliminating SVG’s DOM bloat, heavy matrix inversions, and rigid declarative limits.
Write dynamic for loops, while loops, if/else branches, and user-defined def functions directly inside the graphics source.
Replaces SVG's computationally heavy endpoint-to-center elliptical arc matrix inversions with direct center-radius-angle forward trigonometry.
Built-in time and t clock variables enable deterministic procedural oscillations, rotations, and organic waveforms out of the box.
Integrated 64-bit pseudo-random number generator with seed <N> and random(min, max) for 100% reproducible generative art.
Evaluates entire scenes directly into flat contiguous 2D draw lists with strict loop limits and safe recursion bounds to avoid DoS vulnerabilities.
Embed with <pvg-view> into any modern web framework (React, Vue, Svelte, Astro) with canvas, SVG, or interactive pan/zoom modes.
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
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
<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>
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.
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].
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.
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 |
Select any reference preset to watch the PVG runtime compile and evaluate in real time.
# Loading code...
Standard W3C Custom Element with zero external dependencies.
A clean, indentation-based syntax designed for readability and single-pass parsing.
circle center [x, y], radius r, fill, stroke, width, opacityellipse center [x, y], radius [rx, ry], fill, strokerectangle pos [x, y], size [w, h], radius rline from [x1, y1], to [x2, y2], stroke, widthpolygon points [x1, y1] [x2, y2] [x3, y3] ...group pos [tx, ty], rot θ, scale [sx, sy], opacitystart [x, y] Subpath starting anchor pointline [x, y] Straight segment to destinationquad [cx, cy] [x, y] Quadratic Bézier curvecurve [c1x, c1y] [c2x, c2y] [x, y] Cubic Bézierarc [cx, cy] r start_deg end_deg Forward trig arcclose Connects path back to subpath start180deg (auto-rad), 1.5rad[x + 10, y * cos(a)]is_major ? 18 : 8^ (pow), %, and, or, notsin, cos, tan, sqrt, abs, pow, min, max, floor, ceil, roundseed 42891 & random(min, max)<pvg-view>Measured using PVG's zero-dependency Rust Tracking Allocator and microsecond clock timers.
| 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 |
Jump straight into the web-based IDE with real-time compilation, or integrate PVG into your Rust projects.