LIPI is a compiled, web-native programming language. Python's readability. JavaScript's reach. Zero configuration. Runs entirely in the browser.
# Memory Reinforcement Simulation let memory = {alpha: 0, beta: 0, gamma: 0} let keys = ["alpha", "beta", "gamma"] func reinforce(key) { memory[key] += 14 if memory[key] > 100 { memory[key] = 100 } let bar = $("#mem-" + key) let lbl = $("#lbl-" + key) if bar != null { bar.style.width = memory[key] + "%" lbl.innerText = memory[key] + "%" } } func runSim() { for round in range(6) { for k in keys { reinforce(k) wait(120) } } } runSim()
JavaScript is powerful but verbose. Python is clean but runs on servers. LIPI takes the best of both — Python's readable syntax and JavaScript's direct browser access — and compiles transparently to JavaScript at runtime.
It was designed for one thing: writing logic that interacts with a web page, without fighting the language to do it. No async/await ceremony. No event listener boilerplate. No DOM query soup. Just clean, readable code that does what you mean.
When you're designing an algorithm or building a visual simulation, the language should get out of the way. LIPI has no semicolons, no type annotations, no boilerplate.
HTML element selection and event binding are language primitives in LIPI, not library calls. #box selects an element. on btn.click binds an event. Nothing to import.
Every function in LIPI is internally async. wait(500) pauses execution for 500ms — code after it runs after the pause. No callbacks, no promises, no async/await in your code.
Write bubble sort, BFS, or a memory allocator and watch it animate on screen using DOM updates and wait(). The code reads like a textbook algorithm.
Build state machines, progress systems, or real-time dashboards. LIPI's async model lets you build timed simulations that update the page step-by-step without any framework.
LIPI's syntax is intentionally close to pseudocode. Students learning data structures can write their first algorithm in LIPI with the same mental model they'd use on paper.
Design in the HTML tab. Write behavior in the LIPI tab. No build step. No bundler. Instant preview. The Studio gives you a full-stack environment in the browser.
LIPI is not an interpreter walking an AST node-by-node. It is a compiler that transforms your source code into valid JavaScript, then executes it. This makes it fast and predictable.
Your source text is scanned character-by-character into typed tokens: keywords, identifiers, strings, numbers, operators. Invalid characters produce a LexError immediately.
A Recursive Descent parser with a Pratt (top-down operator precedence) expression engine builds a typed AST. Incorrect syntax causes a ParseError with a line number.
The AST is walked and each node emits JavaScript. All code is wrapped in an async IIFE. User functions are tracked so call sites are automatically awaited.
The compiled JavaScript is executed via new Function() in the page scope. All browser globals are accessible. Errors are caught and displayed with full context.
When LIPI compiles wait(500), it emits await __wait(500) in JavaScript. The surrounding async IIFE pauses at this line for 500ms, then continues — exactly like blocking I/O in Python.
Every function you declare with func is compiled as async function. The compiler tracks all user-declared function names, and every call site to a user function automatically receives await in the generated code.
LIPI has three distinct error types, each thrown at a specific phase. Errors include line numbers and context — you'll never see a cryptic JavaScript stack trace.
A complete language core: variables, loops, functions, objects, async, DOM, events — all with clean, unambiguous syntax.
Dynamic typing. Five primitives: string, number, boolean, null, undefined. Template literals with {expr} interpolation.
Full if / else if / else. Conditions don't need parentheses. Ternary: "yes" if x else "no".
Smart for...in iteration over arrays, objects, numbers. while loops. break and continue supported.
Recursive functions work out of the box. All func declarations are internally async — they can freely call wait().
Full object and array support with dot/bracket access, mutation, deletion, and key iteration.
DOM selection with #id or $(selector). Event binding with on. wait() pauses execution transparently.
| + - * / % | Arithmetic |
| ** | Exponentiation (2**8 = 256) |
| // | Floor division (10//3 = 3) |
| == != < > <= >= | Comparison (strict equality) |
| and or not | Logical (Python-style) |
| += -= *= //= **= | Compound assignment |
| ++ -- | Prefix / postfix increment |
| val if cond else other | Python-style ternary |
These are live LIPI programs running in your browser right now. Edit the code and click Run to see changes instantly.
This demo implements a memory reinforcement algorithm — the same conceptual model used in spaced-repetition learning systems like Anki. Written in ~20 lines of LIPI, it dynamically updates progress bars in real-time.
Each memory slot has a strength score that increases when reinforced and decays over time. The visual bars update live as the algorithm runs. This is exactly the kind of simulation LIPI was built for.
let slots = {alpha:0, beta:0, gamma:0, delta:0, epsilon:0} func reinforce(key) { slots[key] += random(8, 22) if slots[key] > 100 { slots[key] = 100 } updateBar(key, slots[key]) } for round in range(8) { for k in slots { reinforce(k); wait(80) } }
Not a replacement for Python or JavaScript. A focused tool for browser-based logic and visual systems.
| Feature | LIPI | JavaScript | Python |
|---|---|---|---|
| Setup required | ✦ None | ± Build tools optional | ✕ Runtime required |
| Clean, minimal syntax | ✦ Yes | ✕ Verbose | ✦ Yes |
| DOM manipulation built-in | ✦ Language primitive | ± Library required | ✕ Not applicable |
| Async without async/await | ✦ Transparent | ✕ Explicit required | ✕ Explicit required |
| Runs in the browser | ✦ Native | ✦ Native | ✕ Needs Pyodide/WASM |
| Event binding syntax | ✦ on el.click | ✕ addEventListener() | ✕ Not applicable |
| Visual animation (wait) | ✦ wait(ms) | ✕ setTimeout/Promise | ✕ time.sleep (blocks) |
| For-in over objects | ✦ Iterates keys | ± for...in (quirky) | ✦ dict iteration |
| Style shortcuts | ✦ el.style.bg | ✕ Full property names | ✕ Not applicable |
| Compiled (not interpreted) | ✦ AST → JS | ✦ V8 JIT | ± Bytecode |
| Learning curve | ✦ Low (pseudocode-like) | ✕ High (async, this, etc.) | ✦ Low |
The complete LIPI syntax in one scroll. For the full manual, see the documentation.
let x = 42 let s = "hello" let b = true x += 10 x++
if x > 10 { log("big") } else { log("small") }
for i in range(5) { ... } for x in [1,2,3] { ... } for k in obj { ... } while x > 0 { x-- }
func add(a, b) { return a + b } let r = add(3, 4) # 7
let o = {a: 1, b: "hi"} log(o.a) o["c"] = true delete o.b log(has(o, "c"))
let a = [1, 2, 3] append(a, 4) log(len(a)) # 4 log(sum(a)) # 10 log(sort(a))
# By ID (shorthand) let el = #my-box # By CSS selector let el = $("#my-box") # Multiple elements let els = queryAll(".card")
on btn.click { wait(500) el.innerText = "Done" } # sleep = alias for wait sleep(1000)
2 ** 8 # 256 10 // 3 # 3 (floor) `Hi {name}!` # interpolation "y" if x else "n" # ternary
No terminal. No npm install. No webpack config. Open the Studio and start writing.
Press Ctrl+Enter to run. Preview resets and re-executes in under 50ms. No build step, no hot reload delay.
Projects sync to Firestore in real-time with a 1.5s debounce. Your code is safe even if you close the tab. Offline support included.
Two tabs: main.lipi for logic, index.html for layout. A live preview and console sit side by side. Split panes, resizable.
# main.lipi log("🚀 App Starting...") let box = #demo-box let counter = 0 on box.click { counter += 1 box.innerText = "Clicks: " + counter box.style.bg = "hsl(" + (counter * 30) + ",70%,45%)" }
Four patterns that separate reliable code from fragile code.
If the element doesn't exist in the HTML tab, the selector returns null. Accessing .style or .innerText on null throws a runtime error.
getInput() always returns a string. Adding a string to a number concatenates instead of adding. Always use int() or float() first.
LIPI functions are not hoisted at the top level. Declare a function before the line that calls it in the main script flow.
Accessing a key that doesn't exist on an object returns undefined, which can cause downstream bugs. Use has() to check first.
LIPI Studio is free, browser-based, and ready in seconds. No account needed to explore the docs and demos.