Version 0.2 · Measured against React, Svelte, Vue and SolidJS →
Paste one prompt. Your agent builds the app for half the cost.
ArtScript is a full-stack web language designed for AI agents: measured over 50 tasks, Claude builds and changes the same apps for 50% less than in React. Your agent writes a third of the code, the compiler catches its mistakes, and the app ships about 5 KB of JavaScript.−50%cost per working app vs React + TypeScript (Claude Sonnet 5.5)
~5 KBof JavaScript per app (brotli), runtime included. React: ~59 KB
2.9Ktokens: the whole language spec your agent reads
1 linefor a validated REST api with storage: api users: User
Same app. A fraction of the code.
A full-stack users list (form, validation, a REST api and storage) exactly as the eval measured it in each stack. The labels are in Spanish, like the eval's dataset.ArtScript · app.art39 lines · ≈173 tokens
model User {
id: ID
name: String
email: Email
}
api users: User
page Users "/" {
data users = api.users.list()
state name = ""
state email = ""
state error = ""
fn add() {
if !email.includes("@") {
error = "Email inválido"
return
}
await api.users.create({ name, email })
name = ""
email = ""
error = ""
}
column gap=2 {
input name placeholder="Nombre"
input email placeholder="Email"
button "Agregar" primary -> add()
if error != "" {
text error bold
}
for u in users {
row gap=2 {
text `${u.name} (${u.email})`
button "Borrar" small -> api.users.remove(u.id)
}
}
}
}
React · App.tsx + server.ts78 lines · ≈671 tokens
// ---- App.tsx ----
import { useEffect, useState } from "react";
type User = { id: string; name: string; email: string };
export default function App() {
const [users, setUsers] = useState<User[]>([]);
const [name, setName] = useState("");
const [email, setEmail] = useState("");
const [error, setError] = useState("");
const load = () => fetch("/api/users").then((r) => r.json()).then(setUsers);
useEffect(() => { load(); }, []);
async function add() {
if (!email.includes("@")) {
setError("Email inválido");
return;
}
await fetch("/api/users", { method: "POST", headers: { "content-type": "application/json" }, body: JSON.stringify({ name, email }) });
setName("");
setEmail("");
setError("");
load();
}
async function remove(id: string) {
await fetch(`/api/users/${id}`, { method: "DELETE" });
load();
}
return (
<div className="flex flex-col gap-2">
<input placeholder="Nombre" value={name} onChange={(e) => setName(e.target.value)} />
<input placeholder="Email" value={email} onChange={(e) => setEmail(e.target.value)} />
<button onClick={add}>Agregar</button>
{error && <span>{error}</span>}
{users.map((u) => (
<div key={u.id} className="flex gap-2">
<span>{u.name} ({u.email})</span>
<button onClick={() => remove(u.id)}>Borrar</button>
</div>
))}
</div>
);
}
// ---- server.ts ----
import { randomUUID } from "node:crypto";
import { createServer } from "node:http";
type User = { id: string; name: string; email: string };
const users: User[] = [];
createServer(async (req, res) => {
const url = new URL(req.url ?? "/", "http://localhost");
const send = (status: number, body?: unknown) => {
res.writeHead(status, { "content-type": "application/json" });
res.end(body === undefined ? "" : JSON.stringify(body));
};
if (url.pathname === "/api/users" && req.method === "GET") return send(200, users);
if (url.pathname === "/api/users" && req.method === "POST") {
let raw = "";
for await (const chunk of req) raw += chunk;
const { name, email } = JSON.parse(raw);
if (typeof name !== "string" || typeof email !== "string" || !email.includes("@")) return send(400, { error: "Email inválido" });
const user = { id: randomUUID(), name, email };
users.push(user);
return send(201, user);
}
const m = url.pathname.match(/^\/api\/users\/(.+)$/);
if (m && req.method === "DELETE") {
const i = users.findIndex((u) => u.id === m[1]);
if (i >= 0) users.splice(i, 1);
return send(204);
}
send(404, { error: "not found" });
}).listen(Number(process.env.PORT));Everything an app needs, nothing an agent has to guess
The language is small enough to fit in a prompt and complete enough to ship real products.A third of the codeOne line per element, no hooks, no imports, no boilerplate. The same app takes about a third of the tokens of React, so every request an agent makes costs less.
Full stack in one filemodel, api, auth and server fn sit next to the UI. api users: User is a validated REST api with SQLite storage; data loads it and reloads by itself after writes.
Typed end to endThe compiler checks the UI, the api calls against the models, and null safety. Mistakes are caught before the app runs, not by a user.
Errors written for modelsEvery error has a stable code, what was expected, what was found and the fix. art check --ai prints them as JSON, so a retry is one cheap step.
Edits, not rewritesAn agent changes existing code with art patch: small, addressable, typechecked and atomic. It doesn't re-send whole files to rename a button.
About 3 KB of JavaScriptSignals and direct DOM updates, no virtual DOM. A typical app ships 2 to 5 KB brotli including the runtime, against about 59 KB for React.
Secure by defaultHashed passwords, cookie sessions, CSRF protection, rate limits, validation on the server, CSP headers and safe URLs, with nothing to configure.
Real pagesRoutes, layouts, meta tags, guards and prerendered HTML for search engines and AI crawlers. Dark mode and responsive props are built in.
The whole npm ecosystemuse "date-fns" { format } imports any package or your own TypeScript, and the compiler checks the names exist. Charts and maps work through ref and mount.
For AI agents
Errors that tell the model how to fix them
A typo in a field name is caught at compile time. The model gets one JSON line with what was expected and the fix, so the retry costs a few tokens instead of a debugging session.Agents also get an MCP server, a compact project map and a patch format, so they read and write only what changes.for u in users {
text u.emial
}$ art check --ai
{"code":"E1011","type":"UNKNOWN_FIELD","loc":"app.art:13:10","at":"Users","expr":"u.emial","expected":"id|name|email","fixes":["email"]}Measured, not claimed
Claude Sonnet 5.5 built the same apps in every stack. Each app was run and used in a simulated browser; the cost counts the spec, retries and thinking tokens.USD per working appClaude Sonnet 5.5
Dogfooding
This website is written in ArtScript
Every page you are reading, the docs, the benchmarks charts and the playground are 9 .art files with 862 lines, compiled by ArtScript and prerendered to static HTML. The guides are Markdown files from the repository, so people and AI agents read the same source.Start building
Create an app, open it in your editor and ask your agent for the first feature.npm create artscript@latest my-app
cd my-app && npm install
npm run dev