Skip to content

tinyweb

Graphics, widgets and sound on a web page, kept in time with each other.

A program builds its page by calling the functions below: each canvas, button, label and slider goes below the ones before it. A canvas is drawn on with clear, circle, rect, line and text, in pixels from its top left corner, and a color is a CSS color such as “#ff0000” or “red”.

A page has a look of its own, the same wherever it is shown: theme picks one of tinyweb’s, the _style functions change every element of one kind, and set_style and set_colors change one element.

A program that handed the page a function — on_click, on_frame and the rest — keeps running after its last line: the page calls the function each time the event happens. Sound and pictures are kept in step by one clock, time: a frame is drawn for a time, and play can start a sound at one.

tinyweb needs a page to show. A program starts with web_init(port), which serves one and prints its address wherever the program runs — pyk program.pyk, the REPL, or any compiled target — and does nothing in a browser, where the page is there already: on a web page, and beside the program in the notebook and the playground.

import tinyweb

button

def button(title: String, on_click: () -> () = nothing) -> Int

Adds a button below everything already on the page.

Parameters

  • title: the text on the button.
  • on_click: the function to call each time it is pressed; by default nothing happens.

Returns

The button’s number.

Example

import tinyweb as tw
tw.web_init(0)
var count = 0
def pressed():
    count := count + 1
    print("pressed", count, "times")
tw.button("Count", on_click = pressed)

button_style

def button_style(
    background: String = "",
    color: String = "",
    border: String = "",
    radius: Int = -1,
    font: String = "",
    size: Int = -1,
    padding: Int = -1,
) -> ()

Changes every button on the page, those made already and those made later. Only what is named changes; the rest stays as the theme has it.

Parameters

  • background: a CSS color for the button.
  • color: a CSS color for its text.
  • border: a CSS border, such as "2px solid navy" or "none".
  • radius: how round its corners are, in pixels.
  • font: a font, such as "serif" or "monospace".
  • size: the size of its text, in pixels.
  • padding: the space between its text and its edge, in pixels.

Example

import tinyweb as tw
tw.web_init(0)
tw.button_style(background = "#2f6fdd", color = "white", border = "none", radius = 14, padding = 10)
tw.button("Start")
tw.button("Stop")

canvas

def canvas(width: Int, height: Int) -> Int

Adds a canvas to draw on, below everything already on the page. It starts white.

Parameters

  • width: its width in pixels.
  • height: its height in pixels.

Returns

The canvas’s number, which the drawing functions take as c.

Example

import tinyweb as tw
tw.web_init(0)
c = tw.canvas(200, 60)
tw.rect(c, 10, 10, 180, 40, "teal")

canvas_style

def canvas_style(border: String = "", radius: Int = -1) -> ()

Changes the frame of every canvas on the page, those made already and those made later. What is drawn on a canvas is not changed.

Parameters

  • border: a CSS border, such as "3px solid black" or "none".
  • radius: how round its corners are, in pixels.

Example

import tinyweb as tw
tw.web_init(0)
tw.canvas_style(border = "3px solid teal", radius = 12)
c = tw.canvas(200, 80)
tw.rect(c, 20, 20, 160, 40, "teal")

circle

def circle(
    c: Int,
    x: Int,
    y: Int,
    radius: Int,
    color: String = "#000000",
) -> ()

Draws a filled circle on the canvas c.

Parameters

  • c: the canvas, as canvas answered.
  • x, y: the centre, in pixels from the canvas’s top left corner.
  • radius: the radius in pixels.
  • color: a CSS color; black by default.

Example

import tinyweb as tw
tw.web_init(0)
c = tw.canvas(200, 100)
tw.circle(c, 100, 50, 40, "gold")
tw.circle(c, 100, 50, 20)

clear

def clear(c: Int, color: String = "#ffffff") -> ()

Fills the whole canvas c with one color, wiping out what was drawn.

Parameters

  • c: the canvas, as canvas answered.
  • color: a CSS color, such as "#ff0000" or "red"; white by default.

Example

import tinyweb as tw
tw.web_init(0)
c = tw.canvas(200, 60)
tw.clear(c, "navy")

label

def label(text: String) -> Int

Adds a line of text below everything already on the page.

Parameters

  • text: what the label says.

Returns

The label’s number, which set_label takes to change the text.

Example

import tinyweb as tw
tw.web_init(0)
tw.label("Hello from tinyweb")

label_style

def label_style(
    background: String = "",
    color: String = "",
    border: String = "",
    radius: Int = -1,
    font: String = "",
    size: Int = -1,
    padding: Int = -1,
) -> ()

Changes every label on the page, those made already and those made later. Only what is named changes; the rest stays as the theme has it.

Parameters

  • background: a CSS color behind the text.
  • color: a CSS color for the text.
  • border: a CSS border, such as "1px solid gray".
  • radius: how round its corners are, in pixels.
  • font: a font, such as "serif" or "monospace".
  • size: the size of the text, in pixels.
  • padding: the space between the text and its edge, in pixels.

Example

import tinyweb as tw
tw.web_init(0)
tw.label_style(background = "lightyellow", border = "1px solid gold", radius = 6, size = 18, padding = 8)
tw.label("Note: labels can look like this.")
tw.label("And so does every other one.")

line

def line(
    c: Int,
    x1: Int,
    y1: Int,
    x2: Int,
    y2: Int,
    color: String = "#000000",
    width: Int = 1,
) -> ()

Draws a straight line on the canvas c.

Parameters

  • c: the canvas, as canvas answered.
  • x1, y1: where the line starts, in pixels.
  • x2, y2: where it ends, in pixels.
  • color: a CSS color; black by default.
  • width: how thick it is, in pixels; 1 by default.

Example

import tinyweb as tw
tw.web_init(0)
c = tw.canvas(200, 100)
tw.line(c, 0, 0, 200, 100)
tw.line(c, 0, 100, 200, 0, "red", 4)

load_sample

def load_sample(name: String) -> Int

Starts loading a sound file, so that play can play it without a delay. The file is looked for beside the page first, then among the samples tinyweb ships: kick1.wav, snare1.wav, hihat1.wav and piano_c1.wav.

Parameters

  • name: the file’s name, such as "kick1.wav".

Returns

The sample’s number, which play takes.

Example

import tinyweb as tw
tw.web_init(0)
kick = tw.load_sample("kick1.wav")
def hit():
    tw.play(kick)
tw.button("Kick", on_click = hit)

on_frame

def on_frame(draw: (Float) -> ()) -> ()

Calls draw before each frame the page shows, about 60 times a second, with the time the frame is for. This is how a program animates.

Parameters

  • draw: the function to call; it is given the frame’s time in seconds, as time reads it.

Example

import tinyweb as tw
tw.web_init(0)
c = tw.canvas(200, 60)
def draw(t: Float):
    tw.clear(c)
    x = int(t * 50.0) - int(t * 50.0) / 200 * 200
    tw.circle(c, x, 30, 10, "orange")
tw.on_frame(draw)

on_key

def on_key(pressed: (String) -> ()) -> ()

Calls pressed each time a key is pressed while the page has the focus.

Parameters

  • pressed: the function to call; it is given the key: a letter or digit as itself, and otherwise the browser’s name for it, such as "ArrowLeft", "Enter" or " ".

Example

import tinyweb as tw
tw.web_init(0)
shown = tw.label("press a key")
def pressed(key: String):
    tw.set_label(shown, "you pressed " ++ key)
tw.on_key(pressed)

on_mouse

def on_mouse(c: Int, click: (Int, Int) -> ()) -> ()

Calls click each time the canvas c is clicked, with where the click was.

Parameters

  • c: the canvas, as canvas answered.
  • click: the function to call; it is given x and y in pixels from the canvas’s top left corner.

Example

import tinyweb as tw
tw.web_init(0)
c = tw.canvas(200, 100)
def click(x: Int, y: Int):
    tw.circle(c, x, y, 5, "red")
tw.on_mouse(c, click)

page_style

def page_style(
    background: String = "",
    color: String = "",
    font: String = "",
    size: Int = -1,
    padding: Int = -1,
    gap: Int = -1,
) -> ()

Changes the page itself: its background, and the text and spacing everything on it shares. Only what is named changes; the rest stays as the theme has it.

Parameters

  • background: a CSS color for the page.
  • color: a CSS color for its text.
  • font: a font, such as "serif", "monospace" or "Georgia".
  • size: the size of its text, in pixels.
  • padding: the space around everything on it, in pixels.
  • gap: the space between one element and the next, in pixels.

Example

import tinyweb as tw
tw.web_init(0)
tw.page_style(background = "#fdf6e3", color = "#586e75", font = "serif", size = 18, gap = 16)
tw.label("Solarized, with room to breathe")
tw.button("OK")

play

def play(
    sample: Int,
    at: Float = 0.0,
    volume: Float = 1.0,
    rate: Float = 1.0,
) -> ()

Plays a sample that load_sample loaded.

Parameters

  • sample: the sample’s number, as load_sample answered.
  • at: when to start, in seconds on the page’s clock (see time), which counts from when the page started rather than from now: two seconds from now is at = tw.time() + 2.0, while at = 2.0 is already past once the page has been open two seconds. A time already past, such as the default 0.0, means now.
  • volume: how loud, as a multiple of the sample’s own volume; 1.0 by default.
  • rate: how fast, as a multiple of normal speed; 2.0 plays it twice as fast and an octave higher. 1.0 by default.

Example

import tinyweb as tw
tw.web_init(0)
piano = tw.load_sample("piano_c1.wav")
def chord():
    start = tw.time()
    tw.play(piano, at = start)
    tw.play(piano, at = start + 0.25, rate = 1.25)
    tw.play(piano, at = start + 0.5, volume = 0.7, rate = 1.5)
tw.button("Play", on_click = chord)

play_file

def play_file(name: String) -> ()

Plays a sound file now, loading it first if nothing has loaded it yet. For a sound played often, load_sample once and then play is quicker.

Parameters

  • name: the file’s name, such as "snare1.wav"; see load_sample for where it is looked for.

Example

import tinyweb as tw
tw.web_init(0)
def hit():
    tw.play_file("snare1.wav")
tw.button("Snare", on_click = hit)

rect

def rect(
    c: Int,
    x: Int,
    y: Int,
    width: Int,
    height: Int,
    color: String = "#000000",
) -> ()

Draws a filled rectangle on the canvas c.

Parameters

  • c: the canvas, as canvas answered.
  • x, y: the top left corner, in pixels from the canvas’s top left corner.
  • width, height: the size in pixels.
  • color: a CSS color; black by default.

Example

import tinyweb as tw
tw.web_init(0)
c = tw.canvas(200, 100)
tw.rect(c, 20, 20, 160, 60, "#4caf50")

set_colors

def set_colors(
    id: Int,
    color: String = "",
    background: String = "",
) -> ()

Changes the colors of one button, label or slider. It wins over the theme and over the _style functions.

Parameters

  • id: the element, as button, label or slider answered.
  • color: a CSS color for its text, or for a slider’s track and handle.
  • background: a CSS color behind it.

Example

import tinyweb as tw
tw.web_init(0)
ok = tw.button("OK")
cancel = tw.button("Cancel")
tw.set_colors(ok, color = "white", background = "seagreen")
tw.set_colors(cancel, color = "white", background = "firebrick")

set_label

def set_label(id: Int, text: String) -> ()

Replaces the text of a label.

Parameters

  • id: the label’s number, as label answered.
  • text: the new text.

Example

import tinyweb as tw
tw.web_init(0)
status = tw.label("waiting")
def pressed():
    tw.set_label(status, "pressed")
tw.button("Press me", on_click = pressed)

set_style

def set_style(
    id: Int,
    background: String = "",
    color: String = "",
    border: String = "",
    radius: Int = -1,
    font: String = "",
    size: Int = -1,
    padding: Int = -1,
    accent: String = "",
    width: Int = -1,
) -> ()

Changes how one element is drawn: a button, a label, a slider or a canvas. It wins over the theme and over the _style functions. Only what is named changes.

Parameters

  • id: the element, as the function that made it answered.
  • background: a CSS color behind it.
  • color: a CSS color for its text.
  • border: a CSS border, such as "2px dashed orange".
  • radius: how round its corners are, in pixels.
  • font: a font, such as "serif" or "monospace".
  • size: the size of its text, in pixels.
  • padding: the space between its text and its edge, in pixels.
  • accent: a CSS color for a slider’s track and handle.
  • width: its width, in pixels.

Example

import tinyweb as tw
tw.web_init(0)
tw.button("Normal")
big = tw.button("Important")
tw.set_style(big, border = "2px dashed orange", radius = 0, size = 22, padding = 12)
title = tw.label("A title")
tw.set_style(title, font = "serif", size = 28)

slider

def slider(
    low: Int,
    high: Int,
    value: Int,
    on_change: (Int) -> () = ignore,
) -> Int

Adds a slider below everything already on the page.

Parameters

  • low, high: the smallest and largest values it can be set to.
  • value: where it starts.
  • on_change: the function to call with each new value it is moved to; by default nothing happens.

Returns

The slider’s number.

Example

import tinyweb as tw
tw.web_init(0)
shown = tw.label("50")
def moved(value: Int):
    tw.set_label(shown, string(value))
tw.slider(0, 100, 50, on_change = moved)

slider_style

def slider_style(
    accent: String = "",
    width: Int = -1,
    color: String = "",
    font: String = "",
    size: Int = -1,
) -> ()

Changes every slider on the page, those made already and those made later. Only what is named changes; the rest stays as the theme has it.

Parameters

  • accent: a CSS color for the slider’s track and handle.
  • width: how long the slider is, in pixels.
  • color: a CSS color for the value shown beside it.
  • font: a font for that value.
  • size: the size of that value’s text, in pixels.

Example

import tinyweb as tw
tw.web_init(0)
tw.slider_style(accent = "crimson", width = 300, size = 20)
tw.slider(0, 100, 70)

text

def text(
    c: Int,
    x: Int,
    y: Int,
    words: String,
    color: String = "#000000",
    size: Int = 16,
) -> ()

Writes text on the canvas c.

Parameters

  • c: the canvas, as canvas answered.
  • x, y: the top left corner of the text, in pixels.
  • words: the text to write.
  • color: a CSS color; black by default.
  • size: the height of the letters in pixels; 16 by default.

Example

import tinyweb as tw
tw.web_init(0)
c = tw.canvas(200, 60)
tw.text(c, 10, 10, "Score: 42", "purple", 24)

theme

def theme(name: String) -> ()

Gives the whole page one of tinyweb’s looks: its colors, fonts, and how buttons, sliders and canvases are drawn. A page starts in "light", which looks the same wherever it is shown. Call it before any of the _style functions, since it forgets what they changed.

The themes are "light", "dark", "plain" (the browser’s own buttons and sliders, on white), "paper" (ink on warm paper, with a serif font) and "retro" (green on black, in a typewriter font). A name that is none of these changes nothing, and says so with debug.

Parameters

  • name: the theme.

Example

import tinyweb as tw
tw.web_init(0)
tw.theme("dark")
tw.label("A dark page")
tw.button("Press me")
tw.slider(0, 10, 3)
c = tw.canvas(160, 40)
tw.circle(c, 20, 20, 12, "orange")

time

def time() -> Float

Reads the page’s clock. Once sound has started this is the audio clock, so a time read here can be given to play to start a sound exactly then.

Returns

The time in seconds.

Example

web_init

def web_init(port: Int) -> ()

Starts the program’s page. Run as pyk program.pyk or compiled, it serves the page on port and prints the address to open; in a browser, the notebook or the playground the page is already there and it does nothing. Call it before any other tinyweb function.

Parameters

  • port: the port to serve on; 0 picks any free one.

Example

import tinyweb as tw
tw.web_init(0)
tw.label("The page is ready.")

Without Pyking

Each of tinyweb’s implementations is also a library of its own, usable from a program written in that language with no Pyking at all: Python, JavaScript, OCaml, C, Java. tinyweb without Pyking has the overview.