ouragora.run / docsOpen console ↗

DOCUMENTATION / Start here

What is Ouragora?

A console on the internet where you can write little programs, build games and tools, and explore what other people have made.

Ouragora is the place. Ouro is the programming language. The console gives you a prompt, a text editor, a compiler, and a shared filesystem. You type commands to find files and run programs. You write Ouro to make something new.

You do not need to install anything or create an account. Programs run in your browser; the server stores the shared files and connects programs to each other.

Two ways to start

Just looking around? Open the console and type these commands, one line at a time:

cat /README
ls /
ls /bin
ls /lib

cat reads a file. ls lists a directory. The contents of /bin depend on what visitors have added. Read a program with cat /bin/name.ouro before running it with name.

Want to build something? Follow Your first program, then try drawing with gfx.

What can you build?

Text tools with the standard library, pixel art and games with gfx, textured graphics with gpu, connected programs with net, or tools that launch other tools with proc.

A shared space

Files you save are readable by other visitors. Your browser holds the key that lets you change files you created. Do not store passwords or private information here. See Files & ownership before leaving something behind.

DOCUMENTATION / Your first program

Write, save, run

Make a program that prints a message. This is the same workflow you will use for the package examples.

1. Open a file

In the console, type:

edit /bin/my-hello.ouro

Choose another name if that path already belongs to someone. .ouro is the file extension for Ouro source code.

2. Write your program

Paste this into the editor, not into the command prompt:

fn main(): i64 {
    print("hello from ouragora\n");
    return 0;
}

main is where execution begins. print writes text to the terminal; \n starts a new line. return 0; ends the program successfully.

3. Save and close

Press Ctrl+S (or ⌘S on Mac), wait for the saved message, then press Esc to return to the prompt. F1 opens the editor guide.

4. Run it

my-hello

You should see hello from ouragora. A bare command name looks for /bin/name.ouro. You can also use the full path:

run /bin/my-hello.ouro

Edit the message, save, and run again. The console compiles the saved source each time you run it. Press Ctrl+C to stop a running job.

DOCUMENTATION / Ouro basics

A little Ouro

Ouro is a statically typed language: variables and function arguments have types, and the compiler checks them before your program runs.

Variables, loops, and conditions

fn main(): i64 {
    let count: i64 = 1;
    while count <= 3 {
        print_i64(count);
        print("\n");
        count = count + 1;
    }
    if count == 4 {
        print("done\n");
    }
    return 0;
}

This prints 1, 2, 3, then “done”. let declares a variable; it can be reassigned. Statements end with semicolons. Conditions and loops use braces. Comments begin with //.

Types you will meet

i64Whole numbers, such as a score or pixel coordinate.
f64Decimal numbers, such as 1.5. Used by math and gpu.
booltrue or false. Use and, or, and not.
stringText in double quotes.
*ArenaA pointer to a memory arena. Packages use this to allocate their data.

Functions

fn double(n: i64): i64 {
    return n * 2;
}

fn main(): i64 {
    print_i64(double(6));
    print("\n");
    return 0;
}

The type after a function's closing parenthesis is its return type. Convert between numeric types explicitly, for example 12 as f64.

Imports and memory

The standard library is loaded automatically: print, print_i64, and arena_new need no import. Packages use import math; and qualified names such as math::sqrt(9.0).

arena_new(16777216) creates an arena with a 16 MiB capacity. A pointer type begins with *; null means no value was returned. The examples check for failed allocations. &e passes the address of a variable so a function can fill it in.

DOCUMENTATION / Console commands

Finding your way around

Type these at the console prompt. They are commands, not Ouro code.

CommandWhat it does
helpList the built-in commands. ? also works.
ls /binList programs visitors have saved.
cd /libChange your current directory.
pwdPrint the current directory.
cat /READMERead a text file.
edit /bin/my-tool.ouroCreate or edit a file. write is an alias.
cp /bin/tool.ouro /bin/my-tool.ouroMake your own copy.
rm /bin/my-tool.ouroDelete a file you own.
run /bin/my-tool.ouroCompile and run saved source.
whoamiShow your anonymous identity, not the secret key.
clearClear the terminal output.

Paths

A path starting with / is absolute. Otherwise it is relative to your current directory. After cd /lib, ls gfx lists /lib/gfx. Use cd / to return to the root.

Keyboard shortcuts

Ctrl+C stops a running job and its child programs. Ctrl+L clears terminal output. In the editor, Ctrl+S / ⌘S saves and Esc closes. If you have unsaved changes, the editor warns before discarding them.

DOCUMENTATION / Files & ownership

Your files in a shared world

Saving a file makes it available to everyone. Ownership controls who can change it, not who can read it.

Where things live

/READMEThe welcome file.
/binPrograms that can be run by name.
/libShared packages and their source.
/sys/stdThe built-in standard library source.

Use ls / to discover other folders that visitors have created.

Making a copy

cp /bin/their-tool.ouro /bin/my-tool.ouro
edit /bin/my-tool.ouro

Replace the source name with a file that exists, and choose an unused destination. Copying gives you a separate file to edit; it does not change the original.

Your browser is your identity

The first browser to create a path owns it. Its anonymous key is stored in that browser. Clearing site data, switching browser profiles, or using another device can leave you unable to change your old files. whoami shows a short identifier; it is not a recovery key.

The server retains previous versions when files are overwritten or deleted, but the console has no built-in restore command. Keep your own copy of work you care about.

DOCUMENTATION / Package overview

Choose a package

Packages are reusable Ouro code. These five are bundled with the console; you do not need to install them.

PackageUse it for
gfxPixel art, simple games, shapes, text, keyboard and mouse input. A good first graphics package.
gpuTextured sprites, triangles, transparency, and depth with WebGL2.
mathTrigonometry, square roots, clamping, and interpolation.
netFind open relay ports and connect programs inside one browser page.
procLaunch other Ouro programs, capture output, and host child windows.

How to use one

import math;

fn main(): i64 {
    print_i64(math::sqrt(81.0) as i64);
    print("\n");
    return 0;
}

Save this in an .ouro file and run it. It prints 9. The import loads the package; :: selects a function inside it.

Read the source

ls /lib
ls /lib/math
cat /lib/math/ouro.json
cat /lib/math/math.ouro

ouro.json describes the package. Its .ouro files contain the implementation and API comments. Functions marked pub are available to callers. Shared package contents may change; their source is the reference for the version in your console.

DOCUMENTATION / gfx · pixels & input

Draw your first screen

Use gfx for a small pixel canvas, shapes, and a built-in bitmap font. Coordinates are whole numbers measured from the top-left corner.

Save this as /bin/my-picture.ouro, then run my-picture. It draws a green rectangle and a message. Press Esc to exit.

import gfx;

fn main(): i64 {
    let a: *Arena = arena_new(16777216);
    if a == null { return 1; }
    let s: *gfx::Screen = gfx::open(a, 320, 200);
    if s == null { return 1; }
    let e: gfx::Event;
    while true {
        while gfx::poll(s, &e) {
            if e.kind == gfx::KEY_DOWN and e.a == gfx::K_ESC {
                gfx::close(s);
                return 0;
            }
        }
        gfx::clear(s, 0x101010);
        gfx::rect(s, 40, 50, 100, 60, 0x66cc99);
        gfx::text(s, 8, 8, "hello, pixels", 0xffffff);
        gfx::present(s);
    }
    return 0;
}

The drawing loop

Poll input first, clear the old image, draw the new one, then call present to display it. Colours use 0xRRGGBB: 0xff0000 is red and 0xffffff is white.

Useful calls

gfx::pixel(s, x, y, color);
gfx::rect(s, x, y, width, height, color);
gfx::circle(s, center_x, center_y, radius, color);
gfx::line(s, x0, y0, x1, y1, color);
gfx::text(s, x, y, "text", color);

These are call patterns to use inside your program. After polling, gfx::key(s, gfx::K_LEFT) tells you whether the left arrow is held. s.mouse_x, s.mouse_y, and s.buttons[0] give mouse state.

Read more: cat /lib/gfx/gfx.ouro.

DOCUMENTATION / gpu · accelerated graphics

Draw with the GPU

Choose gpu for sprites, transparency, and larger scenes. It needs WebGL2 in your browser. Unlike gfx, drawing coordinates use decimal numbers.

Save as /bin/my-gpu.ouro and run my-gpu. This draws a blue rectangle. Press Esc to exit.

import gpu;

fn main(): i64 {
    let a: *Arena = arena_new(16777216);
    if a == null { return 1; }
    let g: *gpu::Gpu = gpu::open(a, 640, 360);
    if g == null { return 1; }
    let e: gpu::Event;
    while true {
        while gpu::poll(g, &e) {
            if e.kind == gpu::KEY_DOWN and e.a == gpu::K_ESC {
                gpu::close(g);
                return 0;
            }
        }
        gpu::clear(g, 0x101010);
        gpu::rect(g, 40.0, 60.0, 180.0, 100.0, 0x6699ff);
        gpu::text(g, 16.0, 16.0, "hello, gpu", 0xffffff, 2.0);
        gpu::present(g);
    }
    return 0;
}

Going further

gpu::texture(g, pixels, w, h) uploads a buffer of u32 pixels in 0xAARRGGBB format and returns a texture ID. Use that ID in gpu::sprite(g, id, x, y, w, h).

gpu::alpha(g, 128) applies roughly half opacity to subsequent drawing. gpu::depth(g, true) enables depth testing. gpu::tri draws a triangle; gpu::vertex supports textured vertices with depth.

Read the full signatures and texture setup in cat /lib/gpu/gpu.ouro.

DOCUMENTATION / math · numbers

Numbers, angles, and motion

Math helpers for movement, geometry, and calculations. Functions take and return f64 values.

import math;

fn main(): i64 {
    let distance: f64 = math::sqrt(9.0 + 16.0);
    print_i64(distance as i64);
    print("\n");
    return 0;
}

Save and run using the first-program workflow. The result is 5; the cast converts the result to an integer for print_i64.

CallMeaning
math::clamp(x, lo, hi)Keep a value between two bounds.
math::lerp(a, b, t)Interpolate: t = 0 gives a; t = 1 gives b.
math::sin(angle), math::cos(angle)Waves and circular motion. Angles are in radians.
math::atan2(y, x)Find the angle of a direction.
math::sqrt(x), math::pow(x, y)Square root and exponentiation.
math::floor(x), math::ceil(x), math::round(x)Round a value; the result is still f64.

Also included: abs, min, max, tan, atan, exp, and log. Read cat /lib/math/math.ouro for implementation details.

DOCUMENTATION / net · connections

Connect running programs

Networking here connects Ouro programs through Ouragora. It does not fetch websites or connect to arbitrary internet servers.

See whether a port is open

import net;

fn main(): i64 {
    let a: *Arena = arena_new(1048576);
    if a == null { return 1; }
    if net::open(a, 7777) {
        print("someone is listening on 7777\n");
    } else {
        print("port 7777 is not open\n");
    }
    return 0;
}

Send a message between two tabs

Save the following as /bin/my-listener.ouro and run it in one console tab. It waits for a connection, sends a greeting, then exits. If port 7777 is taken, choose another port in both programs.

fn main(): i64 {
    let listener: i64 = socket_listen(7777);
    if listener < 0 { return 1; }
    print("waiting on 7777\n");
    let client: i64 = socket_accept(listener);
    if client >= 0 {
        fd_print(client, "hello from another program\n");
        file_close(client);
    }
    file_close(listener);
    return 0;
}

Save this as /bin/my-client.ouro and run it in a second console tab while the listener is waiting:

fn main(): i64 {
    let a: *Arena = arena_new(1048576);
    if a == null { return 1; }
    let client: i64 = socket_connect("", 7777);
    if client < 0 { print("no listener\n"); return 1; }
    let line: string = "";
    if fd_read_line(a, client, &line) {
        print(line);
        print("\n");
    }
    file_close(client);
    return 0;
}

The socket and file functions are in the standard library, so these two programs need no import. An empty host string selects the relay. Reads can wait for data; fd_ready(fd) checks readiness without waiting.

What net adds

net::ports(a) lists open relay ports. net::listen_local(port) and net::connect_local(port) use private ports reachable only by programs in the same page. Ports close when their program ends.

Read cat /lib/net/net.ouro.

DOCUMENTATION / proc · child programs

Run one program from another

Use proc to combine tools or build a program that manages other programs in the same browser page.

First create /bin/my-hello.ouro from Your first program. Then save this as /bin/my-launcher.ouro and run my-launcher:

import proc;

fn main(): i64 {
    let a: *Arena = arena_new(1048576);
    if a == null { return 1; }
    let child: *proc::Proc = proc::start(
        a, "/bin/my-hello.ouro", null, proc::INHERIT
    );
    if child == null {
        print("could not start the program\n");
        return 1;
    }
    return proc::wait(child.pid);
}

You should see the child's greeting. null supplies no arguments. Waiting keeps the parent alive until the child finishes; ending a parent also ends its children.

Three launch modes

proc::INHERITThe child prints to the terminal and receives no input.
proc::PIPERead from child.stdout and write to child.stdin using file functions.
proc::WINDOWThe parent receives the child's graphics and forwards input. It must display the frames itself.

proc::status(pid) checks a process. proc::wait(pid) waits for its exit code. proc::start_window, proc::frame, and proc::send_event support window hosts.

Read cat /lib/proc/proc.ouro for signatures and pipe/window examples.

DOCUMENTATION / Troubleshooting

When something does not work

“Nothing here answers to that”

The command does not exist as a built-in or as /bin/name.ouro. Check the spelling with ls /bin, or use run /full/path.ouro.

My changes are not running

Save with Ctrl+S / ⌘S and check the editor's save message before pressing Esc. The compiler reads the saved file, not unsaved editor text.

I cannot save over a file

It may belong to another browser identity or be read-only system content. Copy it to an unused path. If you cleared your site data, your previous ownership key may be gone.

A compiler error

Start with the first reported file, line, and column. Check semicolons, braces, argument types, and imports. Use decimal values such as 10.0 for gpu and math arguments. Package names use ::, for example gfx::rect.

A blank graphics screen

Check that opening the screen did not return null and that your loop calls present. The gpu package also requires WebGL2. Try the gfx example to check basic drawing.

A program will not finish

Graphics loops deliberately stay open. Network reads and waits may also block. Use Ctrl+C to stop the job. Programs run in workers, but can still consume your browser's CPU and memory.

What can someone else's program access?

The browser runtime exposes shared files, graphics, input sent to the program, child programs, and the relay. It does not expose the page DOM, cookies, your computer's files, or arbitrary internet connections. Treat text you type into a stranger's program as information they may keep or share.