Cloudflare Pages
- Build Command:
fmp build --web - Output Directory:
dist
The std.web module is Flame’s official framework and compiler toolchain for building high-performance, reactive web applications.
Instead of shipping a heavy runtime framework (like React or Angular) or forcing bulky WebAssembly binaries for simple UI updates, Flame compiles your UI and component declarations directly into clean, fine-grained reactive JavaScript, HTML5, and CSS. When heavy mathematical computations are needed, Flame compiles functions marked with @Wasm into genuine WebAssembly bytecode (dist/app.wasm) and streams it directly to the browser.
The browser’s user interface is fundamentally a JavaScript and DOM environment. Access to the DOM tree, events, animations, and Web APIs are native to browser JavaScript engines.
Flame cleanly divides responsibilities between JavaScript and WebAssembly (WASM):
Flame Source (*.fm) │ Blaze │ @Web Target │ ┌───────────┴───────────┐ │ │ UI / DOM Computation │ │ JavaScript WASM (Fine-Grained) (dist/app.wasm) │ │ └───────────┬───────────┘ ↓ Modern Browser@Wasm): Compiles CPU-intensive routines into native .wasm binaries.@Compute): High-efficiency mathematical evaluation without runtime overhead.web.window, web.document, localStorage).std.net.http client compiled to high-speed asynchronous requests.dist/app.js is typically under 2 KB before minification!Unlike traditional frameworks like React that re-render entire component functions and perform expensive virtual DOM tree diffing on every state change, Flame’s compiler performs fine-grained static dependency tracking:
React (Runtime Overhead):state change ──> re-render component ──> allocate virtual DOM ──> tree diffing ──> DOM mutation
Flame (Zero Virtual DOM):state change ──> known compiler dependency ──> direct DOM node updateBecause Flame knows statically which text nodes and attributes reference @State variables, generated JavaScript modifies only the exact affected DOM text node or attribute:
count changes ──> _t0.textContent = "Counter: " + countThe surrounding <main>, <h1>, and <button> elements are untouched, and no component re-renders take place.
Flame provides 12 specialized web annotations that control compilation, routing, reactivity, layout wrapping, styling, and WebAssembly execution:
| Annotation | Target | Parameters | Description |
|---|---|---|---|
@Web |
Entry Function | title: String, port: Int |
Marks the application entrypoint. Configures the HTML page title and local dev server port. |
@Page |
Function | path: String |
Declares a client-side routable page view (e.g. @Page("/"), @Page("/compute")). Automatically registered in the SPA router. |
@Layout |
Function | children: Node |
Defines an application shell wrapping page content. Preserves persistent chrome across route transitions. |
@Component |
Function | props... |
Declares a reusable UI component that returns JSX elements. Can be instantiated like HTML tags (<NavBar />). |
@State |
let mut var |
None | Declares a fine-grained reactive signal. Mutations directly update dependent DOM nodes. |
@Computed |
Function / Variable | None | Declares a derived reactive calculation. Automatically tracks dependencies on @State variables and recalculates on demand. |
@Effect |
Function | None | Declares a reactive side-effect that automatically executes whenever its referenced @State dependencies change. |
@Compute |
Function | None | Marks high-performance mathematical and algorithmic functions for maximum optimization. |
@Wasm |
Function | None | Compiles heavy computational routines directly into WebAssembly binary bytecode (dist/app.wasm). |
@Style |
Function | None | Declares CSS styling rules compiled directly into dist/app.css without overwriting custom styles. |
@Client |
Function / Block | None | Marks code as strictly client-side browser code for the compiler’s frontend pass. |
@Route |
Function | path: String, method: String |
Declares a server or client route endpoint with explicit HTTP method matching. |
@Web (Application Entrypoint)Marks the entry function as targeting the web browser environment. Accepts optional configuration parameters, supporting both direct file paths and imported resource aliases:
import std.webimport "scrypt.js" as scryptimport "theme.css" as themeimport "style.css" as styleimport "navigation.json" as navData
@Web( title: "Flame Showcase", port: 3000, css: [theme, style], js: [scrypt])fn main() { // Application setup, global state, cursor initialization, and layout definition}| Parameter | Type | Description |
|---|---|---|
title |
String |
Sets the default <title> tag in dist/index.html and the browser window header. |
port |
Int |
Specifies the local development server listening port (defaults to 3000). |
css / styles |
Array or String |
Accepts imported CSS aliases (css: [theme, style]), path strings (css: ["style.css", "theme.css"]), or a single string. Flame copies each stylesheet into dist/ and imports them at the top of dist/app.css using @import "./<file>";, keeping them synchronized alongside any inline @Style declarations. |
js / scripts |
Array or String |
Accepts imported JS aliases (js: [scrypt]), path strings (js: ["custom.js"]), or a single string. Flame copies each script into dist/ and imports them as ES modules at the top of dist/app.js. Exported functions are automatically bound to their alias (e.g. window.scrypt and const scrypt) and to window for immediate execution. |
When you import an external JavaScript module (import "playground.js" as scrypt), you can invoke its functions directly inside Flame event handlers:
export function loadExample(name) { const editor = document.getElementById("code"); if (editor) { editor.value = "// Loaded example: " + name; }}
export function clearEditor() { const editor = document.getElementById("code"); if (editor) editor.value = "";}
export function runFlameCode() { console.log("Compiling and running Flame source...");}import std.webimport "scrypt.js" as scryptimport "theme.css" as themeimport "style.css" as style
fn handleExampleSelect(e: Unknown) { // Read DOM event data directly let selected = e.target.value // Invoke external JS function with arguments scrypt.loadExample(selected)}
fn handleRunClick(e: Unknown) { scrypt.runFlameCode()}
fn handleClearClick(e: Unknown) { scrypt.clearEditor()}
@Web(title: "Flame Playground", css: [theme, style], js: [scrypt])fn main() { @Page("/") fn playground() -> HtmlNode { return ( <div class="playground-app"> <select id="example-select" onChange={handleExampleSelect}> <option value="hello">Hello World</option> <option value="counter">Counter</option> </select>
<button class="btn btn-primary" onClick={handleRunClick}> <span>Run</span> </button>
<button class="btn btn-secondary" onClick={handleClearClick}> <span>Clear</span> </button> </div> ) }}playground.js, discovers loadExample(name), and registers it under scrypt.loadExample.fn loadExample(name: Unknown) -> Unknown) with documentation noting External JavaScript function in playground.js.loadExample jumps directly to line 1 in playground.js.dist/app.js, the compiler generates:
import * as _custom_mod0 from "./playground.js";const scrypt = _custom_mod0;if (typeof window !== 'undefined') window.scrypt = _custom_mod0;playground.js are also registered on window if not already defined.dist/app.css):
theme.css, style.css) are placed into dist/ and prepended via @import "./theme.css"; at the top of dist/app.css.You can also import static JSON or text resources directly into your @Web frontend without writing client-side fetch() boilerplate:
import "navigation.json" as navData
fn main() { // navData is compiled directly as a JavaScript object literal in dist/app.js web.console.log($"Navigation items: {navData.items.len()}")}@Page (Client-Side SPA Routing)Binds a function as a routable page view mounted at the specified URL path. Flame’s compiler automatically extracts all @Page annotations across the workspace and injects them into the built-in SPA router:
export @Page("/about")fn about() -> HtmlNode { return ( <div class="page-container"> <h1>About Us</h1> <p>Built with fine-grained Flame reactivity.</p> </div> )}/about via <a href="/about"> or web.navigate("/about"), the page mounts instantly without a full browser reload.@Page function is also usable as a tag-like component (<About />).@Layout (Persistent App Shell)Defines a shared outer layout wrapper for all pages. Receives children as a parameter representing the active page view:
@Layoutfn AppLayout(children: Unknown) -> HtmlNode { return ( <div class="app-layout"> <NavBar /> <main class="page-body"> {children} </main> <Footer /> </div> )}{children} slot dynamically updates with the active @Page DOM tree.@Component (Reusable UI Elements)Marks a function as a reusable UI component that returns JSX elements. Components accept typed parameters as props, which map directly to JSX tag attributes:
export @Componentfn Button(label: String, primary: Bool) -> HtmlNode { return ( <button class={if primary { "btn btn-primary" } else { "btn btn-secondary" }}> {label} </button> )}
// Instantiation:<Button label="Submit" primary={true} />@State (Fine-Grained Reactive Signals)Marks a mutable variable as a reactive signal. When a @State variable is modified, Flame updates only the exact DOM text nodes and attributes that reference it:
@Statelet mut count = 0
fn increment() { count += 1}
<button onClick={increment}> Clicked: {count} times</button>Under the hood, Flame compiles @State variables into signal getters and setters (_getSignal and _setSignal), subscribing DOM text nodes at compile-time with zero virtual DOM overhead.
@Computed (Reactive Derived State)Declares a derived computation that depends on one or more @State variables. Flame’s compiler automatically analyzes the function body, traces all referenced @State variables, and subscribes dependent UI nodes directly to the underlying signals:
@Statelet mut count = 10
@Computedexport fn double_count() -> Int { return count * 2}
@Computedexport fn count_parity() -> String { if count % 2 == 0 { return "Even" } return "Odd"}
// In JSX:<div>Double: {double_count()}</div><div>Parity: {count_parity()}</div>When count changes, double_count() and count_parity() are re-evaluated and their text nodes update automatically.
@Effect (Reactive Side-Effects)Registers a side-effect function that automatically executes whenever its accessed @State dependencies update. Flame statically inspects the function body (including string interpolations and conditions) to register subscriptions:
@Statelet mut query = ""
@Effectexport fn on_query_change() { web.window.console.log($"Search query updated to: {query}")}query is mutated.@Compute (Pure Algorithmic Optimization)Directs the compiler to optimize mathematical and computational algorithms:
@Computeexport fn is_prime(n: Int) -> Bool { if n <= 1 { return false } let mut d = 2 while d * d <= n { if n % d == 0 { return false } d += 1 } return true}
@Computeexport fn collatz_steps(n: Int) -> Int { let mut steps = 0 let mut val = n while val > 1 && steps < 1000 { if val % 2 == 0 { val = val / 2 } else { val = 3 * val + 1 } steps += 1 } return steps}@Wasm (WebAssembly Bytecode Engine)Compiles heavy computational functions directly into WebAssembly binary bytecode (dist/app.wasm).
@Wasmexport fn wasm_fib(n: Int) -> Int { if n <= 1 { return n } return wasm_fib(n - 1) + wasm_fib(n - 2)}
@Wasmexport fn wasm_factorial(n: Int) -> Int { if n <= 1 { return 1 } return n * wasm_factorial(n - 1)}flame build --web or flame run --web, Flame compiles @Wasm functions into genuine WebAssembly MVP binary modules.dist/app.js, Flame sets up an asynchronous streaming loader:
const { instance } = await WebAssembly.instantiateStreaming(fetch("app.wasm"), {});_wasmExports = instance.exports;window.wasm = _wasmExports;web.window.performance.now().@Style (Component & Global CSS Injection)Injects CSS rules into dist/app.css without overwriting custom styles. @Style can return CSS strings, take inline strings as annotation arguments, or reference external CSS files:
// 1. Inline CSS string function (can be declared at top-level or inside @Web main)@Stylefn componentStyles() -> String { ".badge-wasm { background: rgba(16, 185, 129, 0.2); color: #34d399; padding: 0.25rem 0.75rem; border-radius: 9999px; }"}
// 2. Reference an external stylesheet file@Style("editor.css")fn editorTheme() {}Flame bundles all custom stylesheets specified in @Web(css: ...) and all @Style declarations into dist/app.css automatically on build, so developers do not need to manually touch dist/app.css.
@Client (Client-Side Isolation)Explicitly marks functions, event listeners, or code blocks as client-side browser code:
@Clientexport fn on_sign_out() { web.window.localStorage.removeItem("session_token") web.navigate("/login")}@Route (Explicit Route Declaration)Defines an HTTP route endpoint for fullstack or client-side routing with HTTP method constraints:
@Route(path = "/api/v1/health", method = "GET")export fn healthCheck() { // Endpoint logic}Flame provides native JSX-style syntax directly inside the language grammar without needing Babel, TypeScript, or JSX pragma transforms:
<div id="hero" class="container"> <h1 class="title">Welcome to Flame</h1></div>{expr}Dynamic Flame expressions are evaluated inside single curly braces:
<div class="result"> Active Count: {count} Double Count: {double_count()} Calculated: {wasm_fib(10)}</div>for .. in ..)Flame natively supports iterating over arrays and collections directly inside JSX elements:
<nav class="nav-links"> { for e in elem { <a href={getLinkHref(e)} class="nav-link">{e}</a> } }</nav>Loops can also be declared directly within child elements without surrounding braces:
<ul class="task-list"> for task in tasks { <li class="task-item">{task}</li> }</ul>Flame supports $"..." interpolated strings inside event handlers, effects, and signals:
export fn runWasmFib() { let start = web.window.performance.now() let res = wasm_fib(compute_num) let end = web.window.performance.now() let elapsed = end - start wasm_output = $"Fibonacci({compute_num}) = {res}" wasm_speed_badge = $"⚡ WASM executed in {elapsed.toFixed(3)} ms"}Attach event listeners using camelCase event attributes (onClick, onInput, onChange, onKeyDown):
// Direct state mutation:<button onClick={count += 1}>Increment</button>
// Function reference:<button onClick={fetchProjects}>Fetch Data</button>
// Multi-statement block:<button onClick={ count += 1 toggleColor()}>Update</button><button style={"background-color: " + btn_color + "; color: white;"}> Dynamic Button</button>std.web)The std.web package provides comprehensive, type-safe structures mapping to native browser capabilities:
Document (web.document)The browser Document instance provides DOM querying and node creation:
getElementById(id: String) -> ElementcreateElement(tagName: String) -> ElementcreateTextNode(text: String) -> HtmlNodequerySelector(selector: String) -> ElementquerySelectorAll(selector: String) -> [Element]addEventListener(event: String, handler: Unknown)removeEventListener(event: String, handler: Unknown)Window (web.window)The global browser Window instance provides window management, navigation, timers, and dialogs:
addEventListener(event: String, handler: Unknown)removeEventListener(event: String, handler: Unknown)alert(message: String)prompt(message: String, defaultText: String = "") -> Stringconfirm(message: String) -> BoolsetTimeout(callback: Unknown, delayMs: Int) -> IntclearTimeout(id: Int)setInterval(callback: Unknown, delayMs: Int) -> IntclearInterval(id: Int)requestAnimationFrame(callback: Unknown) -> IntcancelAnimationFrame(id: Int)performance.now() -> Floatlocation: Locationhistory: HistorylocalStorage: StoragesessionStorage: StorageinnerWidth: Int, innerHeight: IntConsole (web.console)Standard browser developer tools logging and debugging:
web.console.info(...args: Any): Outputs informational log message to browser console.web.console.log(...args: Any): Outputs general log message to browser console.web.console.warn(...args: Any): Outputs warning message with yellow alert badge.web.console.error(...args: Any): Outputs error message with red alert badge.web.console.debug(...args: Any): Outputs debug level message.web.console.table(data: Any): Displays tabular data as a clean browser console table.web.console.clear(): Clears the browser console output.ElementInteractive DOM element wrapper:
setAttribute(name: String, value: String)getAttribute(name: String) -> StringremoveAttribute(name: String)appendChild(child: Unknown)removeChild(child: Unknown)addEventListener(event: String, handler: Unknown)removeEventListener(event: String, handler: Unknown)focus()blur()Storage (localStorage / sessionStorage)Web Storage API wrapper:
get(key: String) -> Stringset(key: String, value: String)remove(key: String)clear()length() -> IntEvent, MouseEvent, KeyboardEvent, InputEventEvent objects passed to event handlers with event cancellation (preventDefault(), stopPropagation()) and pointer / key coordinates (clientX, clientY, key, code).
std.net.http)Flame Web projects leverage Flame’s standard networking module std.net.http for asynchronous REST API communication:
import std.net.http
export fn fetchLivePosts() { let container = web.document.getElementById("posts-container") if container { container.innerHTML = "<div class='loading'>Loading live posts...</div>" }
// Native asynchronous HTTP GET request let posts = await http.get("https://jsonplaceholder.typicode.com/posts")
if container { container.innerHTML = "" let mut i = 0 while i < 5 && i < posts.length { let p = posts[i] let card = web.document.createElement("div") card.className = "post-card" card.innerHTML = "<h3>" + p.title + "</h3><p>" + p.body + "</p>" container.appendChild(card) i += 1 } }}async function GenerationFlame’s compiler statically analyzes functions and closures. When await is present within any function, event handler, or closure, Flame automatically compiles the declaration into a native JavaScript async function.
Large web applications in Flame are structured into modular components and routes:
my-web-app/├── flame.toml # Project manifest├── src/│ ├── main.fm # App entrypoint (@Web, @Layout, root @Page("/"))│ ├── navbar.fm # Shared navigation bar (export @Component fn NavBar)│ ├── compute.fm # WebAssembly & Math Showcase (@Wasm, @Compute, @State, @Effect)│ ├── about.fm # About page (export @Page("/about"))│ ├── projects.fm # Live API page with std.net.http (export @Page("/projects"))│ └── contact.fm # Form with reactive @State (export @Page("/contact"))└── dist/ # Auto-generated static web bundle ├── index.html # HTML5 entry mounting to #app ├── app.js # Standalone reactive signals + SPA router + compiled UI (<2 KB) ├── app.wasm # Generated WebAssembly binary for @Wasm routines └── app.css # Preserved custom stylesheet + @Style injectionsBelow is the complete, working implementation of a multi-file responsive web application demonstrating all web annotations, WebAssembly, reactive signals, and API integration:
import std.webimport std.net.httpimport navbarimport aboutimport projectsimport contactimport compute
@Web(title: "Flame Web Showcase", port: 3000)fn main() {
@State let mut count = 0
@State let mut btn_color = "#3b82f6"
@Computed fn double_count() -> Int { return count * 2 }
@Computed fn count_parity() -> String { if count % 2 == 0 { return "Even Number" } return "Odd Number" }
fn toggleColor() { if btn_color == "#3b82f6" { btn_color = "#10b981" } else if btn_color == "#10b981" { btn_color = "#ec4899" } else if btn_color == "#ec4899" { btn_color = "#8b5cf6" } else { btn_color = "#3b82f6" } }
fn setupCursor() { web.window.addEventListener( "mousemove", (e) { let dot = web.document.getElementById("cursor-dot") let outline = web.document.getElementById("cursor-outline") if dot { dot.style.left = e.clientX + "px" dot.style.top = e.clientY + "px" } if outline { outline.style.left = e.clientX + "px" outline.style.top = e.clientY + "px" } } ) }
setupCursor()
@Layout fn AppLayout(children: Unknown) -> HtmlNode { return ( <div class="app-layout"> <NavBar /> <div class="app-content"> {children} </div> </div> ) }
@Page("/") fn index() -> HtmlNode { return ( <div class="page-container"> <main class="hero-section"> <div class="hero-badge">Next-Gen Reactive Framework</div> <h1 class="hero-title">High-Performance Web in Flame</h1> <p class="hero-subtitle"> Compile Flame directly into ultra-fast JavaScript DOM code with fine-grained reactivity, WebAssembly (@Wasm), and zero-overhead routing. </p> <div class="interactive-panel"> <button onClick={count += 1} class="btn-counter"> Counter: {count} </button> <button onClick={toggleColor} style={"background-color: " + btn_color + ";"} class="btn-color-toggle"> Active Color: {btn_color} </button> </div> <div class="computed-stats" style="margin-top: 1.5rem; display: flex; gap: 1rem; justify-content: center; flex-wrap: wrap;"> <div style="background: rgba(255, 255, 255, 0.05); padding: 0.5rem 1rem; border-radius: 8px; border: 1px solid rgba(255, 255, 255, 0.1);"> ⚡ <strong>@Computed Double:</strong> {double_count()} </div> <div style="background: rgba(255, 255, 255, 0.05); padding: 0.5rem 1rem; border-radius: 8px; border: 1px solid rgba(255, 255, 255, 0.1);"> 🎯 <strong>Parity:</strong> {count_parity()} </div> </div> </main> </div> ) }}import std.webimport navbar
// 1. @Wasm: Compiled directly to WebAssembly bytecode (dist/app.wasm)@Wasmexport fn wasm_fib(n: Int) -> Int { if n <= 1 { return n } return wasm_fib(n - 1) + wasm_fib(n - 2)}
@Wasmexport fn wasm_factorial(n: Int) -> Int { if n <= 1 { return 1 } return n * wasm_factorial(n - 1)}
@Wasmexport fn wasm_add(a: Int, b: Int) -> Int { return a + b}
// 2. @Compute: High-performance mathematical / numerical algorithms@Computeexport fn compute_is_prime(n: Int) -> Bool { if n <= 1 { return false } let mut d = 2 while d * d <= n { if n % d == 0 { return false } d += 1 } return true}
@Computeexport fn compute_collatz_steps(n: Int) -> Int { let mut steps = 0 let mut val = n while val > 1 && steps < 1000 { if val % 2 == 0 { val = val / 2 } else { val = 3 * val + 1 } steps += 1 } return steps}
// 3. @State: Fine-grained reactive state@Statelet mut compute_num = 10
@Statelet mut wasm_output = "Run WebAssembly computation below"
@Statelet mut wasm_speed_badge = "⚡ Native WASM Engine"
// 4. @Computed: Reactive derived state (tracks dependencies on @State)@Computedexport fn double_compute_num() -> Int { return compute_num * 2}
@Computedexport fn square_compute_num() -> Int { return compute_num * compute_num}
@Computedexport fn compute_num_parity() -> String { if compute_num % 2 == 0 { return "Even" } return "Odd"}
@Computedexport fn prime_status() -> String { if compute_is_prime(compute_num) { return "Yes (Prime)" } return "No (Composite)"}
// 5. @Effect: Automatic reactive side-effect triggered on state updates@Effectexport fn on_compute_num_change() { web.window.console.log($"⚡ [Flame @Effect] compute_num updated: {compute_num}")}
export fn incrementNum() { compute_num += 1}
export fn decrementNum() { if compute_num > 1 { compute_num -= 1 }}
export fn setPrimeTest() { compute_num = 29}
export fn setCollatzTest() { compute_num = 27}
export fn runWasmFib() { let start = web.window.performance.now() let res = wasm_fib(compute_num) let end = web.window.performance.now() let elapsed = end - start wasm_output = $"Fibonacci({compute_num}) = {res}" wasm_speed_badge = $"⚡ WASM executed in {elapsed.toFixed(3)} ms"}
export fn runWasmFactorial() { let start = web.window.performance.now() let res = wasm_factorial(compute_num) let end = web.window.performance.now() let elapsed = end - start wasm_output = $"Factorial({compute_num}) = {res}" wasm_speed_badge = $"⚡ WASM executed in {elapsed.toFixed(3)} ms"}
// 6. @Style: CSS rules compiled directly into application stylesheet@Stylefn compute_styles() -> String { ".compute-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(320px, 1fr)); gap: 1.5rem; margin-top: 2rem; } .compute-card { background: rgba(30, 41, 59, 0.7); border: 1px solid rgba(255, 255, 255, 0.1); border-radius: 16px; padding: 1.75rem; backdrop-filter: blur(12px); box-shadow: 0 8px 32px rgba(0, 0, 0, 0.2); } .badge-wasm { display: inline-flex; align-items: center; gap: 0.4rem; padding: 0.3rem 0.75rem; border-radius: 9999px; background: rgba(16, 185, 129, 0.15); color: #34d399; font-size: 0.85rem; font-weight: 600; border: 1px solid rgba(16, 185, 129, 0.3); } .badge-compute { display: inline-flex; align-items: center; gap: 0.4rem; padding: 0.3rem 0.75rem; border-radius: 9999px; background: rgba(99, 102, 241, 0.15); color: #818cf8; font-size: 0.85rem; font-weight: 600; border: 1px solid rgba(99, 102, 241, 0.3); } .badge-computed { display: inline-flex; align-items: center; gap: 0.4rem; padding: 0.3rem 0.75rem; border-radius: 9999px; background: rgba(236, 72, 153, 0.15); color: #f472b6; font-size: 0.85rem; font-weight: 600; border: 1px solid rgba(236, 72, 153, 0.3); } .compute-val { font-size: 1.5rem; font-weight: 700; color: #f8fafc; margin: 0.75rem 0; font-family: monospace; } .compute-actions { display: flex; gap: 0.75rem; flex-wrap: wrap; margin-top: 1rem; } .btn-calc { background: linear-gradient(135deg, #6366f1, #8b5cf6); color: white; border: none; padding: 0.6rem 1.2rem; border-radius: 8px; font-weight: 600; cursor: pointer; } .stats-row { display: flex; gap: 1rem; margin-top: 0.75rem; flex-wrap: wrap; } .stat-pill { background: rgba(255, 255, 255, 0.05); padding: 0.5rem 1rem; border-radius: 8px; border: 1px solid rgba(255, 255, 255, 0.08); font-size: 0.9rem; }"}
// 7. @Page: Routable Page Viewexport @Page("/compute")fn compute() -> HtmlNode { return ( <div class="page-container"> <div class="content-section"> <h1 class="section-title">⚡ WebAssembly, Compute & Web Annotations</h1> <p class="section-description"> Flame combines fine-grained reactive HTML with high-speed WebAssembly routines and pure mathematical evaluation. </p>
<div class="compute-grid"> // WASM Card <div class="compute-card"> <span class="badge-wasm">@Wasm · WebAssembly</span> <h2 class="card-title">Native WASM VM Engine</h2> <p class="card-desc"> Heavy mathematical routines are compiled directly to WebAssembly binary bytecode and instantiated in the browser. </p> <div class="compute-val">{wasm_output}</div> <div class="stats-row"> <span class="badge-wasm">{wasm_speed_badge}</span> </div> <div class="compute-actions"> <button onClick={runWasmFib} class="btn-calc"> Run Fib({compute_num}) </button> <button onClick={runWasmFactorial} class="btn-calc"> Run Factorial({compute_num}) </button> </div> </div>
// Computed & State Card <div class="compute-card"> <span class="badge-computed">@Computed · Reactive Signals</span> <h2 class="card-title">Reactive Derived Computations</h2> <p class="card-desc"> Automatically tracks dependencies on @State variables and recalculates with zero virtual DOM overhead. </p> <div class="compute-val">Active Value: {compute_num}</div> <div class="stats-row"> <div class="stat-pill">Double: {double_compute_num()}</div> <div class="stat-pill">Squared: {square_compute_num()}</div> <div class="stat-pill">Parity: {compute_num_parity()}</div> </div> <div class="compute-actions"> <button onClick={incrementNum} class="btn-calc"> Increment (+1) </button> <button onClick={decrementNum} class="btn-calc"> Decrement (-1) </button> </div> </div>
// Compute Card <div class="compute-card"> <span class="badge-compute">@Compute · Pure Algorithms</span> <h2 class="card-title">Optimized Math Routines</h2> <p class="card-desc"> Optimized mathematical calculations executed with maximum efficiency. </p> <div class="stats-row"> <div class="stat-pill">Prime Test: {prime_status()}</div> </div> <div class="stats-row"> <div class="stat-pill">Collatz Sequence: {compute_collatz_steps(compute_num)} steps</div> </div> <div class="compute-actions"> <button onClick={setPrimeTest} class="btn-calc"> Test Prime (29) </button> <button onClick={setCollatzTest} class="btn-calc"> Test Collatz (27) </button> </div> </div> </div> </div> </div> )}import std.web
fn getLinkHref(name: String) -> String { if name == "Home" { return "/" } return "/" + name.toLowerCase()}
export @Componentfn Nav(elem: [String]) -> HtmlNode { return ( <header class="navbar"> <div id="cursor-dot" class="cursor-dot"></div> <div id="cursor-outline" class="cursor-outline"></div> <div class="nav-brand"> <span class="brand-icon">⚡</span> <span class="brand-text">Flame Web</span> </div> <nav class="nav-links"> { for e in elem { <a href={getLinkHref(e)} class="nav-link"> {e}</a> } } </nav> </header> )}
export @Componentfn NavBar() -> HtmlNode { return ( <Nav elem={["Home", "About", "Compute", "Projects", "Contact"]} /> )}import std.webimport navbar
export @Page("/about")fn about() -> HtmlNode { return ( <div class="page-container"> <main class="content-section"> <h1 class="section-title">About Flame Web</h1> <p class="section-description"> Flame Web pairs Flame syntax with a reactive DOM runtime. It compiles UI trees into native DOM calls with granular signals. </p> <div class="features-grid"> <div class="feature-card"> <div class="feature-icon">⚡</div> <h3>Fine-Grained Signals</h3> <p>No virtual DOM overhead. When state changes, only the exact DOM node updates.</p> </div> <div class="feature-card"> <div class="feature-icon">🌐</div> <h3>Built-in SPA Router</h3> <p>Multi-page navigation with client-side history state and instant page switches.</p> </div> <div class="feature-card"> <div class="feature-icon">⚙️</div> <h3>Native WebAssembly</h3> <p>Execute heavy mathematical computations at near-native hardware speed via @Wasm.</p> </div> </div> </main> </div> )}import std.webimport std.net.httpimport navbar
export fn fetchProjects() { let container = web.document.getElementById("projects-grid") if container { container.innerHTML = "<div class='loading-card'>⚡ Loading live projects via std.net.http...</div>" } let data = await http.get("https://jsonplaceholder.typicode.com/posts") if container { container.innerHTML = "" let mut i = 0 while i < 6 && i < data.length { let post = data[i] let card = web.document.createElement("div") card.className = "project-card" card.innerHTML = "<span class='project-tag'>Post #" + post.id + "</span><h3 class='project-title'>" + post.title + "</h3><p class='project-body'>" + post.body + "</p>" container.appendChild(card) i += 1 } }}
export @Page("/projects")fn projects() -> HtmlNode { return ( <div class="page-container"> <main class="content-section"> <h1 class="section-title">Live API Projects</h1> <p class="section-description"> Demonstrating native std.net.http integration with jsonplaceholder.typicode.com/posts in Flame. </p> <div class="action-bar"> <button onClick={fetchProjects} class="btn-primary"> Fetch Live Posts via http.get() </button> </div> <div id="projects-grid" class="projects-container"> <div class="empty-state"> Click the button above to load live posts using Flames std.net.http client. </div> </div> </main> </div> )}import std.webimport navbar
@Statelet mut contact_status = "Awaiting your message"
export fn submitContact() { contact_status = "🚀 Thank you! Your message was sent successfully."}
export @Page("/contact")fn contact() -> HtmlNode { return ( <div class="page-container"> <main class="content-section"> <h1 class="section-title">Get in Touch</h1> <p class="section-description"> Send a message through our reactive contact form. </p> <div class="contact-box"> <div class="form-group"> <label>Your Name</label> <input id="contact-name" type="text" placeholder="e.g. Satoshi Nakamoto" class="input-field" /> </div> <div class="form-group"> <label>Message</label> <textarea id="contact-msg" placeholder="Tell us about your project..." class="input-field textarea"></textarea> </div> <button onClick={submitContact} class="btn-primary"> Send Message </button> <div class="status-banner"> Status: {contact_status} </div> </div> </main> </div> )}* { box-sizing: border-box; margin: 0; padding: 0;}body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif; background-color: #0b0f19; color: #f1f5f9; min-height: 100vh; overflow-x: hidden;}.cursor-dot { position: fixed; top: -100px; left: -100px; width: 8px; height: 8px; background: #ec4899; border-radius: 50%; pointer-events: none; z-index: 9999; transform: translate(-50%, -50%); transition: width 0.15s, height 0.15s;}.cursor-outline { position: fixed; top: -100px; left: -100px; width: 34px; height: 34px; border: 2px solid rgba(168, 85, 247, 0.6); border-radius: 50%; pointer-events: none; z-index: 9998; transform: translate(-50%, -50%); transition: transform 0.06s ease-out, border-color 0.2s;}.navbar { display: flex; justify-content: space-between; align-items: center; padding: 1.25rem 2.5rem; background: rgba(15, 23, 42, 0.85); backdrop-filter: blur(12px); border-bottom: 1px solid rgba(255, 255, 255, 0.08); position: sticky; top: 0; z-index: 100;}.nav-brand { display: flex; align-items: center; gap: 0.6rem; font-size: 1.25rem; font-weight: 700; color: #f8fafc;}.brand-icon { font-size: 1.5rem;}.nav-links { display: flex; gap: 1.8rem;}.nav-link { color: #94a3b8; text-decoration: none; font-size: 0.95rem; font-weight: 500; transition: color 0.2s;}.nav-link:hover { color: #38bdf8;}.hero-section { display: flex; flex-direction: column; align-items: center; justify-content: center; text-align: center; padding: 6rem 2rem;}.hero-badge { display: inline-block; padding: 0.4rem 1rem; border-radius: 9999px; background: rgba(56, 189, 248, 0.1); color: #38bdf8; font-size: 0.85rem; font-weight: 600; margin-bottom: 1.5rem; border: 1px solid rgba(56, 189, 248, 0.25);}.hero-title { font-size: 3.5rem; font-weight: 800; line-height: 1.15; margin-bottom: 1.25rem; background: linear-gradient(135deg, #ffffff 0%, #94a3b8 100%); -webkit-background-clip: text; -webkit-text-fill-color: transparent;}.hero-subtitle { font-size: 1.2rem; color: #94a3b8; max-width: 650px; line-height: 1.6; margin-bottom: 2.5rem;}.interactive-panel { display: flex; gap: 1.25rem; flex-wrap: wrap; justify-content: center;}.btn-counter { padding: 0.9rem 2rem; border-radius: 12px; border: 1px solid rgba(255, 255, 255, 0.15); background: #1e293b; color: #f8fafc; font-size: 1.05rem; font-weight: 600; cursor: pointer; transition: transform 0.15s, background 0.2s;}.btn-counter:hover { transform: translateY(-2px); background: #334155;}.btn-color-toggle { padding: 0.9rem 2rem; border-radius: 12px; border: none; color: #ffffff; font-size: 1.05rem; font-weight: 600; cursor: pointer; transition: transform 0.15s, filter 0.2s;}.btn-color-toggle:hover { transform: translateY(-2px); filter: brightness(1.1);}.content-section { max-width: 1000px; margin: 0 auto; padding: 4rem 2rem; width: 100%;}.section-title { font-size: 2.5rem; font-weight: 800; margin-bottom: 0.8rem;}.section-description { font-size: 1.15rem; color: #94a3b8; margin-bottom: 2.5rem; line-height: 1.6;}.features-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); gap: 1.5rem;}.feature-card { background: #131d31; padding: 2rem; border-radius: 16px; border: 1px solid rgba(255, 255, 255, 0.06); transition: transform 0.2s, border-color 0.2s;}.feature-card:hover { transform: translateY(-4px); border-color: rgba(56, 189, 248, 0.4);}.feature-icon { font-size: 2rem; margin-bottom: 1rem;}.feature-card h3 { font-size: 1.25rem; margin-bottom: 0.5rem;}.feature-card p { color: #94a3b8; line-height: 1.5; font-size: 0.95rem;}.action-bar { margin-bottom: 2rem;}.btn-primary { padding: 0.85rem 1.8rem; background: #3b82f6; color: white; border: none; border-radius: 10px; font-weight: 600; font-size: 1rem; cursor: pointer; transition: background 0.2s, transform 0.15s;}.btn-primary:hover { background: #2563eb; transform: translateY(-2px);}.projects-container { display: grid; grid-template-columns: repeat(auto-fill, minmax(280px, 1fr)); gap: 1.5rem;}.empty-state, .loading-card { grid-column: 1 / -1; padding: 3rem; text-align: center; background: #131d31; border-radius: 16px; color: #94a3b8; border: 1px dashed rgba(255, 255, 255, 0.15);}.project-card { background: #131d31; padding: 1.5rem; border-radius: 14px; border: 1px solid rgba(255, 255, 255, 0.08); display: flex; flex-direction: column; gap: 0.6rem;}.project-tag { align-self: flex-start; padding: 0.25rem 0.6rem; background: rgba(168, 85, 247, 0.15); color: #c084fc; border-radius: 6px; font-size: 0.75rem; font-weight: 600;}.project-title { font-size: 1.1rem; font-weight: 700; color: #f1f5f9; text-transform: capitalize;}.project-body { font-size: 0.9rem; color: #94a3b8; line-height: 1.5;}.contact-box { background: #131d31; padding: 2.5rem; border-radius: 16px; border: 1px solid rgba(255, 255, 255, 0.08); max-width: 600px; display: flex; flex-direction: column; gap: 1.5rem;}.form-group { display: flex; flex-direction: column; gap: 0.5rem;}.form-group label { font-size: 0.9rem; font-weight: 600; color: #cbd5e1;}.input-field { background: #0b0f19; border: 1px solid rgba(255, 255, 255, 0.12); padding: 0.8rem 1rem; border-radius: 8px; color: #f8fafc; font-size: 1rem; outline: none; transition: border-color 0.2s;}.input-field:focus { border-color: #38bdf8;}.textarea { min-height: 120px; resize: vertical;}.status-banner { padding: 0.8rem 1.2rem; background: rgba(16, 185, 129, 0.1); border: 1px solid rgba(16, 185, 129, 0.25); color: #34d399; border-radius: 8px; font-size: 0.95rem;}Flame provides a built-in development HTTP server, live-reload watcher, and static bundler:
run --web)Start a local development server:
fmp run --web src/main.fmThis compiles your project into dist/ and serves it at http://localhost:3000/.
run --watch --web)Enable hot reloading during active development:
fmp run --watch --web src/main.fmThe watcher listens for filesystem changes across your source files, automatically recompiles dist/app.js, dist/app.wasm, and dist/app.css, and immediately refreshes the browser without losing scroll state.
build --web)Generate an optimized static distribution:
fmp build --web src/main.fmdist/ OutputWhen Flame builds your web application, it produces a self-contained static directory ready for instant deployment:
dist/├── index.html # HTML5 entry point mounting the app to #app├── app.js # Standalone reactive runtime, router, and compiled UI (<2 KB)├── app.wasm # Native WebAssembly binary module compiled from @Wasm routines└── app.css # Merged styles (custom styles preserved + @Style injections)When you customize dist/app.css, subsequent flame build --web or flame run --web runs never overwrite your custom CSS rules. Instead, Flame merges custom CSS with any @Style declarations in your code.
Deploying your Flame Web application is as simple as hosting the dist/ directory on any static cloud provider:
Cloudflare Pages
fmp build --webdistVercel
fmp build --webdistNetlify
distfmp build --webDocker / Nginx
dist/ directly into /usr/share/nginx/html