Skip to main content

Build native OpenGL Applications with HTML, CSS, and JavaScript

Project description


███╗   ███╗ ██████╗ ██████╗ ██████╗ ██╗  ██╗
████╗ █████║██╔═══██╗██╔══██╗██╔══██╗██║  ██║
██╔████╔██║██║   ██║██████╔╝██████╔╝███████║
██║╚██╔╝██║██║   ██║██╔══██╗██╔═══╝ ██╔══██║
██║ ╚═╝ ██║╚██████╔╝██║  ██║██║     ██║  ██║
╚═╝     ╚═╝ ╚═════╝ ╚═╝  ╚═╝╚═╝     ╚═╝  ╚═╝

Build native OpenGL Applications with HTML, CSS, and JavaScript.

No browser. No Electron. No WebView. Just a lightweight native binary.


License Python C++ OpenGL Version



What is Morph?

Morph is a UI framework that compiles .mx files (JSX-like syntax with CSS and JavaScript) directly into native OpenGL binaries. You write familiar web syntax — Morph produces a lean, native binary with zero browser overhead.

<!-- src/App.mx -->
<morph-window title="My App" width="800" height="600">
  <div style="height: 48px; background: #1a1a2e;">
    <button morph-open="settings">Settings</button>
  </div>

  <h1 style="color: #e0e0e0;">Hello from Morph</h1>
</morph-window>
morph dev      # live window, hot reload via Unix socket
morph build    # optimized native binary

Why Morph?

Electron Qt Morph
Write UI in HTML/CSS/JS C++ / QML HTML/CSS/JS
Runtime Chromium (~150MB) Qt libs Zero
Binary size ~80MB+ ~20MB+ <1MB
Native OpenGL access
Hot reload
Custom C++ nodes

Quick Start

1. Install

pip install levizr-morph
morph doctor          # verify system dependencies

2. Create a project

morph init my-app
cd my-app

3. Start dev mode

morph dev

A native window opens. Edit src/App.mx — the window updates instantly without restarting.

4. Ship

morph build
# builds native binary

How It Works

Morph is a compiler, not an interpreter. Your source files never ship — only the compiled binary does.

src/App.mx ──► MorphParser ──► JSXWalker ──► IRBuilder ──► LayoutEngine ──► IRSerializer
                                                                                    │
                                                   ┌────────────────────────────────┴──────────────┐
                                                   ▼                                               ▼
                                           [Dev: IPC Socket]                              [Build: C++ Codegen]
                                           morph_devrt binary                       node_emitter → g++ → binary

Python handles the entire toolchain — .mx parsing via tree-sitter, IR building, layout math, and Jinja2-based C++ code generation. C++ handles the runtime — OpenGL rendering, window management, and event handling. The final binary contains zero Python and zero Node.

In dev mode, the pipeline produces an IR dict that is sent over a Unix socket to a pre-compiled renderer (morph_devrt) on every file save. The window never closes — only the node tree swaps. In build mode, the same IR dict drives Jinja2 C++ code generation, producing a standalone binary via g++.


Current State (v0.0.6 — Early Development)

✅ Working

Component Status
.mx file parsing — tree-sitter-based JSX, imports, props Complete
CSS parsing — local files, remote URLs, MD5-cached Complete
Tailwind CSS — 500 common utility classes + arbitrary values Complete
IRBuilder — walked AST → IR with inline CSS, Tailwind, color/unit conversion Complete
CLIinit (interactive wizard), dev, build, pkg, doctor, cache Complete
Configmorph.config.json load/save Complete
IR data modelsIRNode, IRWindow, IRPage, IRViewport, IRStyle, IREvent Complete
IR serializer/deserializer — JSON-safe dict for dev socket Complete
Layout engine — box model (margin, padding), vertical stacking, gap, flexbox Complete
Dev file watcher — watchdog-based with debounce Complete
Unix socket IPC — sends IR to dev runtime Complete
C++ node emitter — IR → C++ instantiation code from Jinja2 templates Complete
Build compiler — g++ invocation with conditional FreeType/GLFW/OpenGL flags Complete
Package registry client — fetch, install, manifest parsing Complete
OpenGL 3.3 batch renderer — instanced VAO/VBO/IBO, uniform color + rounded rect SDF Complete
FreeType text rendering — per-size glyph atlas, batch text with kerning Complete
Rounded rectangles — SDF-based border-radius in fragment shader Complete
Font weight support — bold/normal font selection, font-weight CSS property Complete
Style inheritancecolor, font-size, font-weight, text-align cascade from parent Complete
Transparent backgrounds — only render when background-color set Complete
Event systemonClick, MouseDown, MouseUp, MouseMove events Complete
Scrollbar — browser-like drag, wheel, track-click, nested containers Complete
Viewport culling — skip off-screen children in draw + event dispatch Complete
Feature-based compilation#define MORPH_FEATURE_* guards, linker GC Complete
Flexboxjustify-content, align-items, two-pass layout, content-based sizing Complete
text-align — left, center, right with correct container-relative centering Complete
max-width — layout constraint for responsive sizing Complete
Margin/padding side propertiesmargin-top, padding-left, etc. Complete
Cursor supportcursor: pointer (hand), cursor: text (I-beam) Complete
morph_devrt binary — dev mode C++ renderer, Unix socket IPC, hot reload Complete
Border renderingborder-width, border-color, border-style with SDF shader on all elements (div, button, img); border ring batch on top of everything Complete
Image renderingstb_image-backed: PNG, JPEG, WebP, GIF, BMP, TGA, PSD, HDR, PNM, PIC; per-texture batching, border-radius clipping Complete
Box-sizingcontent-box and border-box CSS property Complete
Dev mode auto-build — CMake integration, automatic binary rebuild on missing Complete
Dirty incremental rendering — layout/paint dirty flag propagation via markDirty(), incremental layoutIfNeeded(), selective recordPaintTree(), cached m_displayList replay via executeDisplayList(); compile-time MORPH_FEATURE_DIRTY_RENDERING gate, auto-enabled for dynamic features (scroll, hover, events, cursor) Complete
Window config hot reload — title update on save, node tree swap without restart Complete
DevTools panel — F12 toggle, element inspect (F2/click), box-model overlay (margin/border/padding/content), element info panel Complete
Nested border-radius clipping — stencil-based (GL_INCR) so child clips properly intersect ancestor masks Complete
Runtime margin: auto — dynamic horizontal centering re-resolved on window resize Complete
CSS :hover pseudo-class — class-based hover rules from CSS files; resolved at runtime with snap-style swap or smooth transition; supports color, background-color, margin, padding, border, border-radius, font-size, gap, justify-content, align-items, width, height Complete
CSS transitiontransition-duration / transition-timing-function properties and transition shorthand; per-element config; interpolation of all numeric/color properties with easing (linear, ease-in, ease-out, ease-in-out); string/display properties snap instantly Complete
Body default padding: 8px — replaced UA default margin: 8px so body background fills edge-to-edge without white gaps; no backward-compat baggage (Morph is not a browser); trivially overridable via CSS Complete
Window clear color from body backgroundglClearColor set to body's background-color each frame; fallback to white when body is transparent; eliminates color mismatch glitch during resize Complete
Dev source hash — CMake rebuild triggered when shared runtime files (core/, render/, widgets/, style/) change Complete
Wayland/X11 fallback error — clear message when GLFW window creation fails Complete

🚧 In Progress

Component Status Notes
Style resolver — CSS cascade, specificity, selector matching Stub Only inline + Tailwind work via IRBuilder
JS interpreter — JS event handler → C++ lambdas Stub Only console.log works
Flexbox flex-wrap — row overflow wrapping Partial Content-based sizing works, wrap still in progress
position: relative/fixed — offset positioning, sticky Stub Not yet implemented

Features

CSS Properties (resolved from inline styles, CSS rules, and Tailwind classes)

  • width, height, max-width
  • margin, padding + individual side properties (margin-top, padding-left, etc.)
  • background-color, color (hex, rgb, named)
  • border-radius, border-width, border-color, border-style
  • box-sizingcontent-box, border-box
  • display: flex, flex-direction, justify-content, align-items, flex-wrap, gap
  • position, left, right, top, bottom
  • overflow, cursor
  • font-size (px, %, em, bare numbers), font-weight, text-align
  • color, font-size, font-weight, text-align cascade from parent to children
  • <body> default changed from margin: 8pxpadding: 8px — body background fills window edge-to-edge; no white gaps. Override with any CSS rule.

Pseudo-classes

  • :hover — class-based rules from CSS files (e.g. .btn:hover); style struct snap-copy on mouse enter/leave with correct restoration; all CSS properties above supported (color, background, margin, padding, border, border-radius, font-size, gap, flex alignment, width, height)
  • :hover with transitionstransition-duration, transition-timing-function CSS properties; transition shorthand (all 0.3s ease-in-out); interpolates all numeric/color properties (bgColor, color, margin, padding, border, radius, fontSize, gap, width/height, etc.); strings (display, position, flex-direction) snap instantly; easing: linear, ease-in, ease-out, ease-in-out

HTML Elements

  • div, span, h1h6, p, button
  • <img> — supports PNG, JPEG, WebP, GIF, BMP, TGA, PSD, HDR, PNM, PIC via stb_image; intrinsic aspect ratio; width/height attributes; border-radius clipping
  • <morph-window> — declares a native window

C++ Runtime

  • OpenGL 3.3 core profile batch renderer (instanced VAO/VBO/IBO)
  • Rounded rectangles via SDF fragment shader (radius auto-clamped, border-radius > 100px → 100px)
  • Border rendering — border-width, border-color, border-style via SDF shader on all elements (div, button, img, etc.); border ring batch (m_borderBatch) flushed last, on top of fills, text, and images
  • Stencil-based border-radius clipping for images and child overflow (uses GL_INCR so nested clips properly intersect)
  • box-sizing: content-box / border-box layout modes
  • FreeType text rendering with per-size glyph atlas and word-wrap
  • Font weight support (bold / normal with DejaVuSans-Bold.ttf)
  • Style inheritance cascade (color, font-size, font-weight, text-align)
  • Transparent backgrounds by default
  • onClick, MouseDown, MouseUp, MouseMove event dispatch
  • Scrollbar with drag, wheel, track-click; nested scroll containers
  • Viewport culling for draw + events
  • Feature-based dead code elimination
  • Image rendering — stb_image-backed texture loading (PNG/JPEG/WebP/GIF/BMP/TGA/PSD/HDR/PNM/PIC); per-texture-ID batched draw calls (m_imageBatches: unordered_map<GLuint, vector<ImageInstance>>); border-radius stencil clipping on images
  • Runtime margin: auto — sentinel -1.0f in style + marginAuto[4] flags, re-resolved dynamically on window resize
  • cursor: pointer and cursor: text via GLFW standard cursors
  • :hover pseudo-class — mouse enter/leave detection in dispatchEvent(); style struct snap-copy to/from hoverStyle; m_baseStyle snapshot for correct restoration; optional smooth transitions via HoverTransition system
  • CSS transitions — m_transitionDuration / m_transitionEasing per-node config; onHover() starts interpolation between current style and target; interpolateStyles() lerps all numeric/color/position properties, snaps strings instantly; updateHoverTransition(dt) runs each frame from update()
  • Window clear color set from body background-color each frame (falls back to white); eliminates color mismatch glitch on resize
  • Dirty incremental rendering — DirtyFlag enum (LayoutDirty, StyleDirty, PaintDirty, ScrollDirty, SubtreeDirty); markDirty() propagates flags up the tree; layoutIfNeeded() skips clean nodes; recordPaintTree() only re-records display lists for paint-dirty nodes; executeDisplayList() replays cached m_displayList for all nodes every frame; MORPH_FEATURE_DIRTY_RENDERING compile-time gate; auto-enabled when scroll, hover, events, or cursor features are present

DevTools (morph_devrt only)

  • F12 — Toggle DevTools panel
  • F2 or click "Inspect Element" — Toggle inspect mode
  • Two tabs: Elements (inspect + node info) and Rendering (pipeline diagnostics)
  • Box-model overlay: margin (orange), border (yellow), padding (green), content (blue)
  • Element info panel: tag name, size, position, margin, padding, display, overflow, box-sizing, color (hex swatch), background (hex swatch), font size, font weight, text align
  • Rendering tab: frame counter, total nodes, layout count / skipped / percentage, repainted count / cache hit rate, layout/paint savings percentages (color-coded green/red)
  • Hot reload preserves DevTools state

Project Structure

my-app/
├── src/
│   ├── App.mx            ← entry point (JSX + CSS + JS)
│   └── components/       ← reusable .mx components
├── cpp/                  ← optional custom C++ nodes
│   └── my_widget.h
├── assets/               ← fonts, textures, etc.
├── morph.config.json     ← project config + dependencies
└── dist/
    └── app               ← compiled binary (gitignored)

morph.config.json:

{
  "name": "my-app",
  "entry": "src/App.mx",
  "window": {
    "width": 1024,
    "height": 768,
    "title": "My App"
  },
  "dependencies": {},
  "cpp_sources": []
}

System Requirements

Linux macOS Windows
Python 3.10+ 3.10+ 3.10+
Compiler g++ 11+ clang++ 13+ MSVC / MinGW
OpenGL 3.3+ 3.3+ 3.3+
GLFW apt install libglfw3-dev brew install glfw bundled

Run morph doctor after installing to verify your environment.


Roadmap

v0.1.0 (Next Up)

  • CSS style resolver — Selector matching, cascade, specificity
  • JS interpreter — Full JS expression handling (CallExpression, BinaryExpression)
  • flex-wrap — Row overflow wrapping
  • position: relative / fixed — Offset and viewport-relative positioning
  • margin collapse — Collapsing vertical margins between siblings

Future

  • Multi-window & navigation system
  • <morph-viewport> embedded OpenGL canvas
  • Custom C++ node integration
  • morph-icons (first-party package)
  • morph-animate
  • Windows support
  • VSCode extension (syntax highlighting for .mx files)

Contributing

Morph is in early development. Contributions, ideas, and feedback are very welcome.

git clone https://github.com/levizr/morph
cd morph
pip install -e ".[dev]"
morph doctor

The most impactful areas right now are the CSS style resolver, morph_devrt binary, and JS interpreter. See the Current State section for a full breakdown.

Open an issue before starting on large features so we can align on design.


License

APACHE — see LICENSE.



Built with C++ and Python  ·  Rendered with OpenGL  ·  No browser required

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

levizr_morph-0.0.6.tar.gz (224.8 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

levizr_morph-0.0.6-py3-none-any.whl (256.6 kB view details)

Uploaded Python 3

File details

Details for the file levizr_morph-0.0.6.tar.gz.

File metadata

  • Download URL: levizr_morph-0.0.6.tar.gz
  • Upload date:
  • Size: 224.8 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for levizr_morph-0.0.6.tar.gz
Algorithm Hash digest
SHA256 e3a55e5bd233b1faaf615647f463e8edb508dc28d5e7f8bb1596be1ec09d752c
MD5 d1a023afabedaf5bf9fd4603f796c3a5
BLAKE2b-256 b91738384a1e84dcfa5f4156e55131872e734b5265f94f9990aaa97f678e185a

See more details on using hashes here.

Provenance

The following attestation bundles were made for levizr_morph-0.0.6.tar.gz:

Publisher: python-publish.yml on Levizr/morph

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file levizr_morph-0.0.6-py3-none-any.whl.

File metadata

  • Download URL: levizr_morph-0.0.6-py3-none-any.whl
  • Upload date:
  • Size: 256.6 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for levizr_morph-0.0.6-py3-none-any.whl
Algorithm Hash digest
SHA256 d210957b56f0d81442e0a161da685e7571d78f80d56a1667e16d458bc3e1141f
MD5 3aa19f72d5283af7c5c4eda9c0913d55
BLAKE2b-256 34984aae140bbfcc3c97131f5de403b59b32b7a784e939d95f259a4ae4e28cd6

See more details on using hashes here.

Provenance

The following attestation bundles were made for levizr_morph-0.0.6-py3-none-any.whl:

Publisher: python-publish.yml on Levizr/morph

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page