# ryupold.de > Personal portfolio, blog and project showcase by Michael Scherbakow (ryupold). > Topics: web frontend development, Zig game development, raylib bindings, game jams, board games. > The site itself is built with [Vode](https://github.com/ryupold/vode) ## Author **Michael Scherbakow** — software engineer, hobby game developer and open-source author. - Website: https://ryupold.de - Email: ryupold@gmail.com - GitHub: https://github.com/ryupold - Codeberg: https://codeberg.org/ryupold - Bluesky: https://bsky.app/profile/ryupold.de - Twitter: https://twitter.com/ryupold - LinkedIn: https://linkedin.com/in/michael-scherbakow-a68b7a290 --- ## Open-Source Projects ### Vode - URL: https://ryupold.de/post/vode - GitHub: https://github.com/ryupold/vode - Codeberg: https://codeberg.org/ryupold/vode - npm: https://www.npmjs.com/package/@ryupold/vode - Latest version: 1.11.0 (released July 03, 2026) - License: MIT - Language: TypeScript (100%) - Tags: web, frontend, framework A compact virtual-DOM web framework for minimalist developers. Zero dependencies, no build step beyond TypeScript compilation. Provides full autocompletion via `lib.dom.d.ts`. Suited for single-page applications and isolated components with complex state. **Core concept — the vode tuple:** A `vode` is a lightweight virtual DOM node written as a plain array tuple: ``` [TAG, PROPS?, CHILDREN...] ``` Tag constants (DIV, SPAN, INPUT, etc.) are exported from the library. Props map directly to HTML attributes. Children can be strings, other vodes, or component functions. **Key API:** - `app(containerNode, state, renderFn)` — mounts a vode app onto any DOM element; returns a `patch` function. - `patch(object | fn | asyncFn | asyncGeneratorFn | [])` — applies a deep-merged state update and triggers a re-render. Supports plain objects, sync/async functions, async generator functions (for incremental yields), and view transition arrays. - `defuse(containerNode)` — removes all event listeners and state references from a mounted app (for cleanup/garbage collection). - `memo(depsArray, componentFn | propsFactory)` — memoizes a component or props object; skips re-render when shallow-compared deps are unchanged. - `context(state).path.to.substate` — creates a typed sub-state context for passing to reusable components without exposing the full state. - `mergeClass(...args)` — intelligently merges class values (string, array, or `Record`). - `mergeStyle(...args)` — intelligently merges style values (string or object). - `mergeProps(...args)` — intelligently merges props objects (left to right) - `hydrate(element)` — reads an existing DOM element as a vode (useful for debugging). - `tag(vode)`, `props(vode)`, `children(vode)` — accessor helpers for vode tuples. **Special vode props:** - `onMount(state, element)` — called when an element is first created in the DOM. - `onUnmount(state, element)` — called when an element is removed from the DOM. - `catch(state, error)` — error boundary; returns a fallback vode if a render error occurs in the subtree. **State management:** State is a plain JS/TS object passed to `app`. After mounting, a `patch` function is added to the state object itself. Patching `undefined` for a key deletes it from state. Silent (no-render) mutations can be done by directly setting properties and then calling `patch({})`. **Installation options:** - ESM via unpkg: `import { app, DIV, ... } from 'https://unpkg.com/@ryupold/vode/dist/vode.min.mjs'` - Classic IIFE (ES5): `