ConeScript is the language a world for the 3-D web is written in. A world arrives as ConeScript, is checked, is built on the server and on each visitor's machine, and then runs. This page says what kind of thing a ConeScript text describes, how it differs from a program, what it leaves out on purpose, and how the rest of this manual is laid out and should be read.
A world, not a program
Every language embodies a paradigm: what it represents, and the surroundings it runs in. A general-purpose language such as Cone represents a program: one process acting on memory, whose code runs when something calls it. ConeScript represents a world: shared, alive, placed in space and in time, seen by many people at once through many cameras, on machines that trust neither each other nor the content.
So the differences are not confined to ConeScript's types. They run through the whole language. The ten differences:
| Difference | What it means in the language | Where in this manual |
|---|---|---|
| Logic hunts for what happens | Nothing calls a lantern. Its logic says what it watches for (dusk; someone within 4 m; a condition becoming true, became(c); a condition holding for a time, held(c, for: d)) and decides, when that happens, whether to act. Values defined from other values follow their inputs as a spreadsheet's cells follow theirs. | Subscriptions, Events |
| Time is in the language | Animators move values over time; every duration names its clock; an animator may begin at a moment already past, so a machine that arrives late agrees with one that was there. | Values: time, Animators |
| Space is in the language | Every placed thing has a frame, of a few kinds, and an identity by its path in a tree: pathLanterns.lantern[12].glass. | Parts, Frames |
| Distribution is in the language | A value lives on the client, the server or both; a server-only value is never sent; only acts cross the network, and each visitor's machine shows them its own way. | Distribution |
| Many people at once | Some values exist once per visitor, with a lifetime; some once per camera (the rain round each eye, a speech bubble in each visitor's picture). | Values: tables keyed by being, Distribution |
| Generation | Most of a world is made, not stored: from a recipe and a seed, so every machine makes the same rocks; some values are drawn afresh each run; descriptions become products at the resolution needed. | Construction, Generators |
| Discipline | Blocks that define a value are pure and never act; every block runs within a budget, so one world's script cannot stall a shared server. | Blocks, Bounded running |
| Arrival | Content arrives into a running world: a visitor joins late, an object is built while others walk about, approved packages from strangers join the vocabulary. | Loading, Packages |
| Perception crosses dimensions | 2D and 3D are composed both ways, and the pointer is routed back through the composition. | Images, Overlays |
| Authoring | Fields carry units, ranges, defaults and descriptions for the tools people build worlds with. | Values: units and ranges, Fields |
The same contrast as a table, a program beside a world:
| A program (Cone) | A world (ConeScript) | |
|---|---|---|
| What runs code | a call | something happening: a message, a change, a clock |
| Time | outside the language | a value, with named clocks and animators |
| Where data is | memory, by address | a tree of placed parts, by path and frame |
| Machines | one process | a server and many visitors, values placed on each |
| Users | one or none | many at once, values per visitor and per camera |
| Data | stored | made from recipes and seeds, at the resolution needed |
| Trust | the program trusts itself | neither machines nor content trust each other: budgets, purity |
| Starting | from nothing | into a world already running |
| Output | whatever it writes | 2D and 3D composed, the pointer routed back |
Principles
Four groups of principles shape the language. Each is stated with its reason, so that an author knows what the language will and will not do for them.
1. The goal: a world as downloadable files
The language captures content creation flexibly enough that a world can be described in files, downloaded and run. Any world is meant to be describable, not only one particular world.
2. Quality and speed, held in balance
The language aims at picture quality against latency, and so at worlds that are:
- high in fidelity: very high-quality worlds are possible;
- small on the wire;
- fast to hydrate into a running world;
- fast to execute once hydrated;
- cheap when far away and rich when near: a far copy of a thing is cheap, a near copy rich.
3. Content says what; the engine decides how
- Content states requirements, never resource choices. The engine may meet them differently by distance or hardware.
- Capability lives in named, composable tools from packages. Authors compose them, and ask package owners for more.
- Mechanisms, not policy. The language provides mechanisms; each world's designer chooses the policy.
- Deterministic. The same description builds the same thing on every machine, so worlds agree and results cache by hash.
4. People work through tools; what is stored is their intent, at its smallest
- The description, the tools it calls, and how descriptions are produced (editors, AI, fitting to a scan) are kept apart. Production happens outside the language.
- An edit is renormalized, never stored as "original plus a change". If an artist draws a balloon's path and later edits it, what is stored is the smallest description of the new path that, sent over the wire, achieves what they want.
- Normal form: no dead steps, a fixed order, the highest-level tool that does the job, every exposed amount kept.
What a world is made of
Six requirements:
- A tree of named parts. A part's path is its identity and its place of ownership, and paths are how parts refer to each other. A part is the language's one unit of organization, as a module is in Cone: a namespace with a path, holding child parts, fields, functions, triggers, materials, layouts, sounds and files (Parts).
- Designs and copies. Any part can be a design, and naming the design makes a copy that follows the design except where it says otherwise (Designs).
- Values and expressions, kept live: a value defined from others is recomputed when its inputs change, the clock among them (Values, Expressions, Subscriptions).
- Handlers: when something happens and a condition holds, change values or send requests to tools and engines (Events, Actions).
- Bounded procedures inside handlers and construction: conditions, loops, named values (Blocks, Control flow).
- A seed, and where a value lives (client, server or both), as properties of generation and state (Distribution).
Values change in exactly three ways: a subscription defines a value from others, derive(path, expression); an event does something once when something happens, on(trigger, action, ...); an animator is built as a value, then started, and moves a value over time in the tick beside physics: one of the library's kinds, or one the world writes as a step function (Animators: your own). Part 3 of this manual covers each.
What ConeScript leaves out, and why
Absent by design: memory management, concurrency, resource strategy, and building new tools. They follow from one principle:
Content states requirements; resources are the implementation's business. Content says how the world looks and behaves. How fast, how much memory, what latency, and how work is divided are decided below, by libraries and the engine.
Content may state priorities and qualities of experience ("this responds instantly when touched"), never mechanisms. Capability lives in the tools, not in the world's own logic: world authors compose the browser's tools; when they need more, the tool is extended and every world gains it.
So, compared with Cone, ConeScript has no permissions (uni, mut references), no regions or allocators, no borrowing or lifetimes, no raw pointers, no trust blocks, and no threads or actors. What remains of Cone is its fundamentals: local names, expressions, blocks whose last line, written without a semicolon, is their value, functions and closures, if, match, while and each, structs, enums, options, results, lists and tuples. Those are written as in Cone, and the pages of Part 1 say where ConeScript simplifies them.
Layers of logic
Logic in a world comes in layers, each able to do more than the one before. The layers are how the ceiling on world logic (below) is stated: a capability that needs more than the top layers a world's own designs may reach belongs in a package.
| Layer | What it can do | Examples |
|---|---|---|
| Expression | compute a value from other values | a lantern's height from the ground; a glow share for the glass |
| Value over time | a value as a function of the clock | the lamps' schedule; the balloon's ride; the rocking chair |
| Rule | when something happens and a condition holds, do something | come near and the glass wakes; a button; a hard landing |
| Handler and state machine | a design's response to messages, changing its state across several states | a lantern's dusk, dawn and touch; a monster's charge and stagger |
| Procedure | conditions, loops and named values | construction pipelines; a counted loop that places the lanterns |
| Library call by name | hand a whole behavior to a package | a villager's walking and wandering; a flock of forty swallows; a gait |
Relation to Cone
ConeScript's fundamentals are compatible with Cone's: a block, an if or an each written in ConeScript is a Cone block, if or each, minus anything about memory. The world constructs (parts, fields, derive, on, animators, durations that name their clock) have no counterpart in Cone and have their own spellings. Whether the finished notation stays Cone-like is still to be decided; this manual uses one provisional syntax throughout so the pages can be read together.
Cone itself is still the language of what lies below a world: the browser, its engines and its packages are written in Cone, and so would be any approved extension (Packages).
How to read this manual
The order of each page
Every reference page runs in the same order:
- What it is, in plain language.
- How you write it: the syntax, with small examples.
- What it means: when it runs, where it runs, what it may not do.
- How it connects to the rest of the language.
- Open questions: only the real ones.
Examples
A package named in an example is a real package. A line that carries // illustration shows a capability that no package provides yet, written as a vocabulary name.
The syntax, in brief
- Fundamentals use Cone's syntax. Local names are
let x = ...;(fixed) andvar x = ...;(changeable); functions arefn name(a: T, b: U) R { ... }; blocks are in braces, statements end with a semicolon, except a last line that gives the block's value, which is written without one. - World constructs have their own spellings, all of them minimal forms. A part is declared
part name, then optionally: what(a call that makes a shape, a light or a sound, or a design to copy), then named options separated by commas:part lamp: lanternHead(panel: p), at: (x, y, z), rotation: rotation(0.3 rad). Its body (fields, child parts, rules, calls) is indented under it, and registrations with the engines are calls in that body that name the part,area(self, volume). - A field is
field name: type = default, then named options:within: lo ..= hi,mutable: true,common: true,lives: client(orserver,both). - A copy names the design:
part wallLantern1: wallLantern, at: (-0.7, 2.4, 0), or calls it with field values,lanternHead(panel: p). - An amount per duration on a clock is a
rate:rate(0.02, per: sim(1 s))(Values: rates). - One argument rule everywhere. A parameter without a default is required and may be given by position, in declared order, or by name; a parameter with a default must be named. Positional arguments come first, and after the first named argument every argument is named. A function that takes any number of like values takes them as one list:
together([a, b])(Expressions: the argument rule). - Other spellings:
derive(path, expr);on(trigger, action), which takes one action, so several become an acting block,on(trigger, { a; b; }); durationssim(0.4 s),world(15 min),real(0.2 s); pathsa.b.c, numbered partslantern[12], the parentself.parent; field accesspart.field: one dot, Cone's, reaches a part's children and its fields alike. - Inside
deriveandon, a block may be written in Cone form, in braces. - Names follow Cone's rules: camelCase (
lampLevel,lanternHead) (Expressions). - Comments start with
//everywhere: in world declarations and inside Cone-form blocks.
Minimal forms first
The language starts minimal: every capability is first a plain named function with parameters, and declarations take named options by the same argument rule. Shorthand (sugar) will be added later, only where use shows it helps: where a form is used often and a shorter form reads or writes more clearly.
A short example in both halves of the syntax, a world's lamp level and its lanterns:
field lampLevel: fraction = 0, within: 0 ..= 1, mutable: true // the world's; every lamp reads it
on(dusk, start(transition(lampLevel, to: 1, over: world(15 min), begin: dusk.due)))
on(dawn, start(transition(lampLevel, to: 0, over: world(15 min), begin: dawn.due)))
part pathLantern // a design: no frame of its own
field near : bool = false, lives: client // each visitor's own (derived: not mutable)
field nearGain : number = 1.45, within: 1 ..= 2 // fixed: a named number
field gain : number = 1, within: 1 ..= nearGain, mutable: true
field gainTarget: number = 1, within: 1 ..= nearGain // derived: not mutable
part post: stake(), material: materials.lanternBlack
area(self, sphere(radius: 4 m, at: (0, 0.2, 0)))
derive(near, occupied(post, by: me))
derive(gainTarget, { // a value block in Cone form
let lit = lampLevel > 0;
if near && lit { nearGain } else { 1 }
})
on(created, start(chase(gain, toward: gainTarget, speed: rate(nearGain - 1, per: sim(0.4 s)))))
The first lines are world constructs; the braces hold an ordinary Cone block whose last line, written without a semicolon, is its value. (gainTarget is written out as its own value only to show a block; the if could equally sit inside the chase.)
The parts of this manual
- Part 1, Fundamentals: values, expressions, blocks, functions, control flow and text: the Cone-compatible core, plus what a world adds to it (units, clocks, positions, colors, tables keyed by being, value blocks versus acting blocks, structured dialogue).
- Part 2, Parts and the world tree: parts, fields, designs and copies, frames, loading.
- Part 3, Behavior: subscriptions, events, actions, animators, package behaviors, visitors and their actions.
- Part 4, Construction: pipelines, sculpting, generators, open functions, fitted products, terrain.
- Part 5, Appearance: materials, textures, lights, sky and weather, effects, sounds.
- Part 6, The 2D half: images, layouts, overlays.
- Part 7, What the engines take: physics, cameras.
- Part 8, The browser and the network: the vocabulary and packages; distribution.
- Appendix: worked examples, and every page's open questions gathered.
Open questions
- The encoding. Whether ConeScript is finally written as a layer of Cone or as a notation of its own is decided after the requirements are settled; the paradigm already differs from Cone's at each of the ten points.
- Which differences are language and which are vocabulary. Time and space could be mostly library (clocks and frames as types); distribution and purity cannot, because they are rules about what code may do.
- The ceiling on world logic: how far up the layers of logic a world's own designs may go before a capability belongs in a package.