Kokkoja as of 2026-10-10: the teacher's requests, the Session window, notices, the QR code, the license

This commit is contained in:
ack committed 2026-10-10 19:01:20 +02:00
1 parent 95c80d04c9
commit 1d087af633
32 files changed
+4917 -890

No files matched your search

+144 -83
View File
@@ -14,22 +14,28 @@ chat message, a text message. It is a link of this shape:
The part after `join=` is the invitation itself, which the host's Kokkoja
may also give you on its own, without the link around it. Both forms
work wherever an invitation is asked for. The invitation carries the key
work wherever an invitation is asked for. A host in the same room may
instead show you the link as a QR code on their screen: point your
phone's or tablet's camera at it and open the link it offers. The invitation carries the key
that protects the session, so pass it on only to people who belong in the
session, and treat it as you would a meeting link. An invitation is good
for one session: the host's next session has a new one.
## Joining
Open the link in a web browser. Firefox, Brave and Chrome are known to
work; other browsers built on the same engines very probably do. You see
Kokkoja's start screen with the invitation already filled in. Enter the
name the others should see and press **Join**. That is all: nothing is
installed, and there is no account to make.
Open the link in a web browser. Firefox, Brave and Chrome on a
computer, and Safari on an iPad or an iPhone, are known to work; other
browsers built on the same engines very probably do. You see
Kokkoja's start screen, headed "Join a session", with the invitation
already filled in. Enter the name the others should see and press
**Join**. That is all: nothing is installed, and there is no account to
make. (Opened without an invitation, the same screen also offers to
start a session of your own; an invitation is a request to join one
session, so with one it offers joining alone.)
Your name is remembered by the browser for next time. So is a second
field, **Notebook for the pages of this session**, which you can leave as
it is for now; what it does is explained under "Your notebook" below.
choice, **Notebook for the pages of this session**, which you can leave
as it is for now; what it does is explained under "Your notebook" below.
If you have the Kokkoja program installed on your computer (see
[A host's Kokkoja](host.md) for what that is and why you might want it),
@@ -42,15 +48,17 @@ place in it waits for the host to return.
## The screen
Across the top is the toolbar. On the left are the tools and their
settings; on the right, the zoom and, for the host, the buttons that
start pages. Below it, the page fills most of the window, with a panel on
the right. Every button explains itself: rest the pointer on it for a
second and its explanation appears beneath it.
Across the top is the toolbar, one row holding everything: the tools
and their settings, the view, the host's page buttons, your notebook,
and **Session**, which opens a window with the participants and the
invitation. Below it the page has the whole width of the window. Every button
explains itself: rest the pointer on it for two seconds and its
explanation appears beneath it.
### The page
The page is an A4 sheet, shown so that it fits the window. Write
The page is an A4 sheet lying on its side, shown so that it fits the
window. Write
anywhere on it. When your writing comes near an edge, the sheet grows in
that direction, in small steps, so there is always room, and the view
keeps its scale and extends beyond the window; scroll to reach the rest.
@@ -65,11 +73,12 @@ is scrolled to concern nobody else.
**Pen** is chosen when you arrive. Draw with a pen on a tablet, a mouse,
or a finger on a touch screen. With a pen, pressing harder makes the
line wider. The colours are to the right of the tools: black, blue, red
and green, and **+** opens a palette of more. A colour chosen from the
palette joins the row of colours and stays there in this browser. The
three lines to the right of the colours are the line widths: 0.4, 0.7
and 1.2 mm at ordinary pressure.
line wider. The colours are to the right of the tools: black, red,
green, blue and white, and **+** opens a palette of more, in which the
colours already in the row leave their places empty. A colour chosen
from the palette joins the row and stays there in this browser. White
is for painting over. The four lines to the right of the colours are
the line widths: 0.4, 0.7, 1.2 and 2.4 mm at ordinary pressure.
If a pen is in use, a hand resting on a touch screen is ignored.
@@ -78,17 +87,25 @@ If a pen is in use, a hand resting on a touch screen is ignored.
**Text** types on the page. Tap where the text should begin and type; a
text field appears there and the text is laid out on the page as you
type. Enter begins a new line. Press Escape, or tap somewhere else, to
finish; a block with nothing in it disappears.
finish; a block with nothing in it disappears. You need not finish to
move a block: rest the pen on it while typing, and it follows the pen,
field and all; lift, and go on typing.
A block made by a tap does not wrap: its lines are as long as you type
them, and the page grows to hold them. To make the text wrap at a chosen
width, drag along where its top edge should run instead of tapping: the
block begins at the left end of the drag and wraps at the right end.
them, and the page grows to hold them; a red corner mark of 5 mm at its
top left shows where it begins. To make a *text rectangle*
instead, drag it open where you want it: a red box, white inside, in
which the text wraps at the box's width, and which grows downward by
lines when you type more than it holds. The box is part of the page,
seen by everyone, and where it lies over an image it covers the image
and nothing else; a teacher uses one to say "write your answer here".
Tap your own text to change it. While a block of yours is being edited
it is drawn grey, and three handles appear: the one at its top-left
corner and the line along its top edge move it, the one at the right end
of that line changes its width. You cannot change anyone else's text.
Tap your own text to change it, anywhere in a box, and the cursor is at
the end of the text. To move a block of yours, rest the pen
on it, without moving, for a little over half a second: a green outline
appears, and it follows the pen. To resize a text rectangle, rest the
pen near one of its corners and drag the corner; the text wraps anew at
the new width. You cannot change anyone else's text.
With the Text tool, the row of colours stays and the line widths give way
to the font, one of **Sans**, **Serif** and **Mono**, and the size in
@@ -102,48 +119,79 @@ whole stroke goes; across a text block and the block goes. It removes
nothing of anyone else's, and nobody else's eraser can touch yours. An
erased stroke is gone from the page for everyone.
### Zoom
### The view
**Fit** shows the whole page in the window, and keeps doing so as the
page grows by writing, except while you are in the middle of a stroke of
your own. **+** and **−** zoom in and out by a tenth. Holding Ctrl and
turning the scroll wheel zooms about the pointer. When the page is
larger than the window, scroll it as you would any page. When the host
adds an image, the view keeps its scale rather than shrinking to fit; a
new image at the bottom of a long page is reached by scrolling.
A page is shown whole when you first see it. Afterwards the view keeps
its scale: when the page grows, or the host adds an image, the page
extends beyond the window and you scroll to the rest. Three buttons and
two gestures change the view, and none of them changes the page:
* **Fit** shows the whole page in the window.
* **A4** shows the page at the scale a new page has, one A4 sheet
filling the window, so that a page shrunk to a small exercise is seen
at its size on the sheet rather than enlarged to fill the window.
* When the host presses **Fit** or **A4**, every participant's view
follows, each in their own window. Your own Fit and A4 change only
your screen.
* **Widen**, which only the host and co-hosts have, makes the page
itself wider, for everyone, until it fills the
window at the present zoom. The sheet grows equally on both sides,
so nothing on the page moves and nothing is rescaled; there is simply
more room at either side.
* **Shrink**, also for the host and co-hosts, makes the page, for everyone, just large enough for what is on it,
with the usual margin around. Nothing on it moves either.
* **Extend** is a box, ticked to begin with. Ticked, the sheet grows
when your writing comes near an edge, so there is always room.
Unticked, your writing stops short of the edge; a stroke that leaves
the page resumes when the pen comes back. Images always get the room
they need.
* Hold **Ctrl** (Command on a Mac) and turn the scroll wheel to zoom in
and out about the pointer.
* Hold **Ctrl** (Command on a Mac) and draw a stroke with the pen: up
zooms in, down zooms out, by twice for every 100 pixels of movement,
about the point where the pen touched down. Nothing is drawn while
the key is held.
If you reload the browser tab, it comes back to the page as you last saw
it: same zoom, same place.
### The panel
### Session
**Participants** lists everyone in the session, with *you* and *host*
marked, and *away* beside anyone who has left or lost their connection;
an absent participant's writing stays on the page. The box in front of
each name shows or hides that participant's writing on your screen alone.
Hiding your teacher's corrections to try an exercise again, or hiding
your own answers to see the exercise as it was, changes nothing for
anyone else.
**Session** in the toolbar opens a window with the people and the
invitation. **Participants** lists everyone in the session, with *you*
and *host* marked, and *away* beside anyone who has left or lost their
connection; an absent participant's writing stays on the page. The box
in front of each name shows or hides that participant's writing on your
screen alone. Hiding your teacher's corrections to try an exercise
again, or hiding your own answers to see the exercise as it was,
changes nothing for anyone else.
**Invitation** shows the invitation of this session, with a button to
copy it, in case someone else should be invited.
copy it and, below, as a QR code for a camera, in case someone else
should be invited. **Log** is a record of what happened in this
session, pages and connection and every press and release of the pen,
with a button to copy it: if something goes wrong, copy it and send it
to whoever looks after Kokkoja. Above it the window says which page
you are on, counted from the start of the session, how you stand with
the relay, and when the page was last saved; below it, where your
notebooks are kept.
**Current notebook**, **Keep page** and **Export** concern your notebook,
below. Under them, a line reports what was last saved, and whether
anything went wrong.
### Notices
The last line of the panel reports what your pen or mouse is delivering:
the kind of device, the pressure, and how many positions per second it
sends. It is there for curiosity and for finding faults.
While all is well Kokkoja says nothing. When there is something to
know, a line appears between the toolbar and the page: in red while
a problem lasts, in grey for a few seconds after something was done.
*Not connected. Reconnecting…* means the relay cannot be reached;
Kokkoja reconnects by itself, and what you wrote meanwhile is sent when
it returns. *The page could not be saved* means the notebook is out of
reach; the page is still in memory and the saving is tried again.
*Waiting for the host's page* means the host has not started a page
yet, or your connection is down. The grey notices say that a page was
kept or exported, and where.
### Status
At the right end of the toolbar, **Page 3** is the number of the page you
are on, counted from the start of the session, and beside it *Connected*,
*Connecting…* or *Disconnected* says how you stand with the relay. If
the connection drops, Kokkoja reconnects by itself; what you wrote while
disconnected is sent when it returns. Until the host has started a
page, the toolbar says *Waiting for the host's page*.
For finding faults, the browser's console (F12) keeps a short log of the
session's events: pages, images, the connection, and what your pen or
mouse delivers.
## Your notebook
@@ -162,24 +210,28 @@ last changed and the number counting the pages kept that day.
Two buttons bring pages out of the browser:
* **Keep page** keeps a dated copy of the page as it is now, and
* **Keep** keeps a dated copy of the page as it is now, and
downloads it, so you find it among your browser's downloads. Pressing
it again before anything has changed makes no second copy.
it again before anything has changed makes no second copy and no
second download. Any mark counts as a change, even a dot from a
stray touch of the pen.
* **Export** downloads a picture of the page as you see it, as a PNG
file; writing you have hidden is left out. Its name is the date and
the page number.
A page file is an SVG file. Any web browser displays it, and vector
drawing programs such as Inkscape open it. Kokkoja itself can load one
again, writing and all, when someone with the right to start pages
chooses it (see "When the host gives you more" below, and the host's
again, writing and all, when the host or a co-host chooses it (see "When the host gives you more" below, and the host's
part).
The notebook field on the start screen names the notebook within the
The notebook list on the start screen names the notebook within the
browser's storage. Giving each course or each colleague a notebook of
their own keeps their pages apart; the name proposed is simply
*Notebook*. **Current notebook** in the panel shows the notebook in use
and lets you change it during the session.
their own keeps their pages apart; until you make one, everything goes
into the notebook *General*. The last entry of the list, **New
notebook…**, asks for a name and makes a notebook of that name; it
exists from the moment a page is written into it. **Current notebook**
in the toolbar is the same list, and lets you change notebook, or make
one, during the session.
### On your computer
@@ -193,7 +245,7 @@ to open the invitation in the installed program.
With the program, a notebook is a directory in `Kokkoja` in your
Documents directory. The page you are on is `Current.svg` there, and
kept pages lie beside it under their dated names. **Keep page** makes
kept pages lie beside it under their dated names. **Keep** makes
the dated copy; **Export** opens Kokkoja's own file chooser, in which you
pick a directory and a name for the PNG file.
@@ -210,35 +262,44 @@ still on the page, from the others' copies and from your notebook, and
it is still yours to erase or change. A tab that was in a session and is
reloaded goes straight back in.
## When the host gives you more
## When the host makes you a co-host
Normally only the host puts images on the page, starts new pages, or
loads pages from a notebook. The host can give you the same right, for
instance in a discussion among colleagues where everyone brings
material. When that happens, three more buttons appear at the right of
your toolbar:
loads pages from a notebook. The host can make you a *co-host*, which
gives you the same powers, for instance in a discussion among
colleagues where everyone brings material. The Session window then
says *co-host* beside your name, and three more buttons appear at the
right of your toolbar:
* **New** starts a new blank page for everyone. The page being left is
kept in each person's notebook.
* **Image** puts an image file on the page for everyone, below any
images already there. Your browser's file chooser asks for the file
(with the Kokkoja program, Kokkoja's own chooser does, beginning where
the notebooks are, with a choice whether an image wider than the page
is shrunk to fit it). A PNG or JPEG file keeps its exact pixels unless
the notebooks are). A PNG or JPEG file keeps its exact pixels unless
it is very large; other formats and very large images are converted
and reduced before they are shared. An image is placed at its natural
size, centred, 8 mm from the top or from the image above; one wider
than the sheet is scaled down to fit between the margins. To put an
image on a page of its own, press **New** first.
size, centred, 5 mm below the image above or at the top of the page;
one wider than the page widens it. To put an image on a page of its
own, press **New** first.
* **Notebook** starts a new page for everyone from a page kept in one of
your notebooks, with everything that was written on it. A chooser
shows your notebooks and small pictures of their kept pages. The kept
page itself is not changed: the session works on a copy.
* **Hide**, among the tools, hides part of the page for everyone: drag
open a rectangle and whatever lies under it, writing included, is
covered in white, marked by blue corners so that nobody writes there
unknowingly. Rest the pen on it to move it, near a corner to resize
it; a tap of the eraser removes it and uncovers what was there. A Hide
stays even for someone who hides your writing on their own screen.
An image cannot be moved or removed once anyone has written on it or
added anything after it, since the marks on it would make no sense
without it. The one exception is a mistake caught at once: the person
who added an image can take it back with a tap of the eraser on it, as
long as nothing else has happened on the page since.
As a co-host you can also move and scale images, as the host can: rest the pen on an image for a little over half a second
and it follows the pen; rest it near a corner and the corner follows,
the shape kept. Writing stays where it is, so images are arranged
first and written on afterwards. A new image always arrives below the
others, centred, at its own size, to be arranged by hand. A tap of the
eraser on an image removes it; writing on it stays. An image you added
yourself you may always remove, even after you have stopped being a
co-host.
The right stays with you until the host withdraws it or the session ends.
You stay a co-host until the host decides otherwise or the session ends.
+142 -47
View File
@@ -22,21 +22,44 @@ You need:
`python3 --version`; if Python is not installed, macOS offers to
install the command line developer tools, which include it. Any Python
from 3.9 on will do.
* **A web browser.** Firefox, Brave or Chrome; other browsers built on
the same engines very probably work.
* **A web browser.** Firefox, Brave or Chrome, or Safari on a Mac;
other browsers built on the same engines very probably work.
* **A pen tablet**, if you want to write by hand. A mouse works for
pointing and for rough marks.
Get Kokkoja from its repository, either by downloading it as a zip file
and unpacking it, or with git:
Get Kokkoja from its repository: download
https://git.andykopra.com/ack/kokkoja-rel/archive/main.zip
and unpack it, or, if you have git,
git clone https://git.andykopra.com/ack/kokkoja-rel.git kokkoja
Nothing else is installed and nothing is changed on your system. To
update later, download again or run `git pull` in that directory. To
update later, download again or run `git pull` in that directory.
Kokkoja is free software under the GNU General Public License; the
`LICENSE.md` file in the directory is the license. To
remove Kokkoja, delete the directory; your notebooks are elsewhere and
stay.
### On a Mac
The same steps, with four things to know:
* Terminal is in Applications → Utilities. The first `python3` or
`git` command may make macOS offer to install the command line
developer tools; accept, and run the command again when it has
finished.
* The first time the program writes a notebook, macOS asks whether
Terminal may reach your Documents folder. Allow it, once; the
notebooks are there. The answer can be changed later under System
Settings → Privacy & Security → Files and Folders.
* The program opens Kokkoja in your default browser, Safari included.
To use another, start the program with `--no-browser` and open the
address it prints in the browser you prefer.
* Wherever this documentation says Ctrl for zooming, use Command on a
Mac. Both work, so a hand used to Ctrl is not punished.
## Starting Kokkoja
Open a terminal in the Kokkoja directory and run
@@ -57,8 +80,13 @@ The relay address names the *Kokkoja server* you use. A server does two
things: it passes the participants' messages, encrypted so that it
cannot read them, and it serves the web site that your guests join from.
The address above is Andy's server; whoever runs a server for you gives
you its address. If you give the program no relay, it expects one on
your own computer, which is useful only for trying Kokkoja alone.
you its address. The program remembers it, so from the second time on
python3 kokkoja.py
is enough, and the program says which relay it is using. Give the
option again only to change servers. Started for the first time with no
relay, it stops and asks for one.
Other options, all optional:
@@ -73,28 +101,34 @@ Other options, all optional:
## Starting a session
On the start screen, enter your name and the notebook for this session,
and press **Start a session**. You are on page 1, a blank sheet, and the
panel on the right shows the invitation.
and press **Start a session**. You are on page 1, a blank sheet, and
**Session** in the toolbar shows the invitation.
### The notebook
The notebook you name is a directory under `Kokkoja` in your Documents
directory, made when it is first needed, and the start screen offers the
names of the notebooks you already have. For teaching, a notebook per
student works well: everything a student is given, with everything they
and you wrote on it, gathers in that student's notebook, and the next
lesson with that student starts by naming it again. Your library of
exercises can live in notebooks of its own, such as *Exercises A1*, filled
by keeping pages in a session of your own with nobody else present.
A notebook is a directory under `Kokkoja` in your Documents directory.
The start screen lists the notebooks you have; until you make one, there
is *General*, the notebook for whatever has no notebook of its own. The
last entry of the list, **New notebook…**, asks for a name and makes a
notebook of that name, which exists from the moment a page is written
into it. For teaching, a notebook per student works well: everything a
student is given, with everything they and you wrote on it, gathers in
that student's notebook, and the next lesson with that student starts
by choosing it again. Your library of exercises can live in notebooks
of its own, such as *Exercises A1*, filled by keeping pages in a session
of your own with nobody else present.
The notebook can be changed during a session, in **Current notebook** in
the panel. The page you are on then continues in the new notebook.
The notebook can be changed during a session, in the notebook list in
the toolbar. The page you are on then continues in the notebook chosen.
### The invitation
**Invitation** in the panel shows a link. **Copy** copies it; send it to
**Session** in the toolbar opens a window; **Invitation** there shows a
link. **Copy** copies it; send it to
your guests by email, in a chat message, or however you reach them. A
guest opens it, enters a name and is in. A guest with the Kokkoja
guest opens it, enters a name and is in. Below the link the same
invitation stands as a QR code: a guest with a phone or a tablet points
its camera at your screen, and the link opens there. A guest with the Kokkoja
program installed may paste it into their own program instead. The link
carries the secret that protects the session, so send it only to those
who should be there; anyone with the link can join and read the page.
@@ -115,24 +149,72 @@ notebook, by itself, under a dated name; nobody has to remember to save.
file chooser opens, beginning where your notebooks are and remembering
where you last chose from; a directory path can also be typed into it.
It shows small pictures of the image files it finds. Choose one and it
is placed on the page: the first at the top of the sheet, each further
one 8 mm below the lowest image there, centred, at its natural size. An
image wider than the sheet is scaled down to fit between the margins, so
a full-page A4 scan appears at about 92%; the chooser has a setting to
turn that off, in which case the sheet widens to the image instead. PNG
and JPEG files are shared as they are unless very large; other formats,
and images over 2400 pixels on a side or 1.5 MB, are converted to JPEG
and reduced first. A page holds up to 12 images.
is placed on the page in one way always: 5 mm below the lowest image
there, or at the top of the page if there is none, centred across the
page's width, at its natural size, in front of the images already
there. Natural size is the size the file records: a scan arrives at
the size of the paper it came from. A photo from a phone or camera
records no useful size and arrives at a tenth of a millimetre per
pixel. An image wider than the page widens the page on both sides to
hold it. From there you move and scale it by hand, as described next;
there is no other setting to choose. PNG and JPEG files are shared as
they are unless very large; other formats, and images over 2400 pixels
on a side or 1.5 MB, are converted to JPEG and reduced first, which
does not change the size they arrive at. A page holds up to 12 images.
To move an image, rest the pen on it, without moving, for a little over
half a second: a green outline appears, and the image follows the pen
until you lift it. It comes to the front of the other images, which a
hold without moving also does. To scale an image, rest the pen near one
of its corners: a green square marks the corner, and dragging it scales
the image along its diagonal, the opposite corner staying put and the
shape kept. This works with every tool: a dot the pen may have drawn
while resting is removed, and the eraser erases nothing while it rests.
Writing is not attached to images: an image moved from under writing
leaves the writing where it was, so compose the images first and write
afterwards. Only you and co-hosts can move images.
For a page that begins with an image, press **New** and then **Image**.
For a series of exercises, keep pressing **Image** on the same page: the
sheet grows downward and everyone scrolls through them. A new image does
not change anyone's zoom; it appears below, to be scrolled to.
If you chose the wrong file, a tap of the eraser on the image takes it
back, as long as nothing else has happened on the page since: no stroke,
no text, no erasure, no further image, by anyone. After that an image
stays, because the marks made on it would make no sense without it.
A tap of the eraser on an image removes it, whoever added it and
whenever. Writing on it stays where it was, so remove an image before
anyone writes on it unless the writing is meant to stay. The page keeps
the size the image gave it; **Shrink** trims it. A guest who added an
image may remove it, even after they have stopped being a co-host.
**Fit** and **A4**, pressed by you, act on every participant's screen,
each in their own window: the way to bring everyone back to the whole
page. A guest's Fit and A4 change only that guest's screen.
**Hide**, the tool beside the Eraser, covers part of the page for
everyone: drag open a rectangle, and whatever lies under it, writing of
anyone included, is covered in white, marked by blue corners that tell
the students something is there. Use it to cover a solution until the
student has tried, or a part of the exercise for later. Rest the pen on
it to move it, near a corner to resize it; a tap of the eraser removes
it and shows what was covered. Nothing under a Hide is lost, and a
student cannot uncover it by hiding your writing on their screen: a Hide
belongs to the page, not to your writing. A Hide is white in an
exported picture and in the page file as other programs see it.
With the Text tool, a rectangle dragged open is a *text rectangle*: a
red box with a white ground, which covers the image beneath it and no
writing, in which text wraps and which grows downward as it fills. It
is the "write your answer here" box of a worksheet: draw it on the scan
where the answer belongs, and the student types into their own box or
beside yours. Only its owner can type in a box, as with any text.
**Widen**, among the view buttons, makes the page wider for everyone,
until it fills your window at your present zoom: the quickest way to
more room for writing beside an exercise. The sheet grows equally on
both sides, so nothing on the page moves. The page keeps the new width
in everyone's notebook. **Shrink** is its opposite: the page becomes
just large enough for everything on it, images and writing, with the
margin of 5 mm around, which trims a page down to its exercise before you keep
or export it. A blank page cannot shrink.
**Notebook** starts a new page for everyone from a page kept in one of
your notebooks, with everything that was written on it. A chooser shows
@@ -147,14 +229,17 @@ was kept when you left it, and **Notebook** brings it back as a new page.
A lesson might go: **New**, **Image** for the exercise, work, **New**
for a point of grammar, work, **Notebook** to return to the exercise.
## Guests' rights
## Co-hosts
In the **Participants** list, each guest's name has a button **pages** in
front of it. Pressing it lets that guest start new pages, put images on
the page and load pages from their notebook, exactly as you can; the
three buttons appear on their toolbar. Pressing it again withdraws the
right, which otherwise lasts until the session ends. Guests can never erase or
change anyone's writing but their own; that needs no setting.
In the **Participants** list of the Session window, each guest's name
has a box **Co-host** beside it. Ticking it makes that guest a
co-host: they may then start new pages, put images on the page and
arrange them, load pages from their notebook, widen, shrink and hide,
exactly as you can, and the buttons for it appear on their toolbar.
Unticking it takes it back; otherwise it lasts until the session
ends. What stays yours alone: making co-hosts, and bringing everyone's
view to the page with Fit or A4. Guests, co-hosts or not, can never
erase or change anyone's writing but their own; that needs no setting.
## Hiding writing
@@ -169,9 +254,10 @@ picture of the exercise with the corrections alone.
The page you are on is written continuously to `Current.svg` in the
current notebook, and copied to a dated file whenever the session moves
on. **Keep page** makes such a copy now, named for the day and a serial
on. **Keep** makes such a copy now, named for the day and a serial
number, such as `2026_10_06_02.svg`; pressing it again before anything
has changed makes no second copy. Use it to mark a state worth keeping
has changed makes no second copy. Any mark counts as a change, even a
dot from a stray touch of the pen. Use it to mark a state worth keeping
on its own, or to add a finished page to a notebook of exercises.
**Export** saves a picture of the page as you see it, as a PNG file at
@@ -190,7 +276,7 @@ Close the browser tab, or stop the program with Ctrl-C in its terminal.
There is nothing to save: the page is already in your notebook, and in
each guest's. Guests who are still connected can go on writing on the
page among themselves, but no new page can be started without you or a
guest you have given the right, and a guest who arrives after everyone
co-host, and a guest who arrives after everyone
has left finds an empty session. The next lesson is a new session with a
new invitation; load the pages you need from the student's notebook.
@@ -199,13 +285,22 @@ new invitation; load the pages you need from the student's notebook.
* **The browser says the notebooks are locked.** Open Kokkoja from the
full address the program printed when it started, the one with the
token.
* **"Disconnected" in the toolbar.** Kokkoja reconnects by itself as
* **"Not connected" above the page.** Kokkoja reconnects by itself as
soon as the relay can be reached, and sends what was written meanwhile.
If it does not come back, check your connection; the page is safe in
your notebook either way.
* **A guest sees "Waiting for the host's page".** You have not started
a page yet, or your connection is down. The first page is started when
you start the session, so this usually means the latter.
* **Pressure does not work.** The last line of the panel reports what
the browser receives from your pen. If it says *mouse*, the browser is
not seeing the pen as a pen; try the other browser.
* **Pressure does not work.** The browser's console (F12) logs what
the browser receives from your pen at your first stroke. If it says
*mouse*, the browser is not seeing the pen as a pen; try the other
browser.
* **"Address already in use" when starting the program.** A copy of
the program is already running, usually in another terminal window.
Only one can run at a time; use it, or stop it there with Ctrl-C and
start again.
* **Guests cannot connect.** Look at the line `Relay: ...` the program
printed when it started: it names the relay the session goes through.
If it is not your server's address, stop the program and start it
again with `--relay` and the right address.
+68 -20
View File
@@ -37,13 +37,16 @@ page, an image once. There is no central copy.
Two computers behind home routers generally cannot connect to each other
directly, so something in between has to pass the messages. The relay is
the smallest such thing: a WebSocket server with *rooms*. A client
connects to `wss://<server>/relay/room/<room id>`; the relay gives the
connection a number, tells the room that a peer has arrived, and from
then on sends every message it receives from one member to all the
others, stamped with the sender's number. When the last member leaves,
the room is gone. It holds at most 32 members per room and 500 rooms,
and refuses messages over 1 MB; clients split anything larger.
the smallest such thing: a WebSocket server that passes messages among
the members of a session. What a user calls a session, the relay calls a
*room*: the word appears in the relay's code and log and in the address a
client connects to, `wss://<server>/relay/room/<room id>`, and nowhere
else. The relay gives each connection a number, tells the others that a
peer has arrived, and from then on sends every message it receives from
one member to all the others, stamped with the sender's number. When
the last member leaves, nothing of the session remains at the relay. It
holds at most 32 members per session and 500 sessions, and refuses
messages over 1 MB; clients split anything larger.
## Sessions and encryption
@@ -53,7 +56,7 @@ The invitation is
The *secret* is 16 random bytes, made by the host's browser. From it
every client derives two values with HKDF-SHA-256: the *room id*, which
names the room at the relay, and the *message key*, a 256-bit AES-GCM
names the session at the relay, and the *message key*, a 256-bit AES-GCM
key with which every message is encrypted before it is sent and
decrypted on arrival. The relay sees the room id, the members' network
addresses, and the size and timing of encrypted messages. It cannot
@@ -66,7 +69,21 @@ A link invitation wraps the same text:
https://<site>/#join=<invitation>
Browsers never send the part of an address after `#` to the site, so the
secret stays between the host and the guests.
secret stays between the host and the guests. The Session window also
shows the link as a QR code, so that a phone or a tablet in the room can
join by camera; the encoder is Kokkoja's own (`qr_code.js`: byte mode,
error correction level M, any version), checked by the tests against
Nayuki's reference encoder.
The client writes its singular events (pages, images, the connection,
the co-host right, the first input report) to the browser's console
and keeps them, with a trace of every press, release and cancellation
of the pointer (an eraser's release says how many strokes and text
blocks it removed), every change of tool and the touch events, in
memory (`log.js`, the last 400 entries). The Session window shows the record with a Copy button,
so that a guest on a tablet, where there is no console, can send it
when the pen misbehaves. The record stays in the browser tab; nothing
of it is sent anywhere by Kokkoja.
## Identity and layers
@@ -74,7 +91,7 @@ Each participant has a signing key pair (ECDSA on P-256), made in the
browser the first time and then kept, by the local program in its
configuration directory or by the browser in its own storage. The
*participant id* is the first 16 bytes of the SHA-256 hash of the public
key. When a client joins a room it announces itself with a signed
key. When a client connects to a session it announces itself with a signed
`hello` carrying its name and public key; the signature covers the room
id and the peer number the relay stamped on the message, so nobody can
replay it elsewhere or claim another peer's connection. Until a peer has
@@ -89,23 +106,37 @@ kept, a person is the same participant in every session from the same
computer, and their writing on a page loaded weeks later is still theirs.
The host is whoever holds the key whose id is in the invitation. Pages,
images and the list of guests allowed to start pages are accepted only
images and the list of co-hosts are accepted only
from the host (or from an allowed guest, for pages and images).
## Pages
Page coordinates are in units of 0.1 mm. A blank page is an A4 sheet,
2100 by 2970 units. Writing within 8 mm of an edge grows the sheet by
8 mm on that side. An image is placed at its natural size, taken from
Page coordinates are in units of 0.1 mm. A blank page is an A4 sheet
in landscape, 2970 by 2100 units. "Widen" makes the sheet wider, and
"Shrink" makes it the bounding box of everything on the page plus the
greater of the two margins, exactly, on every side; both are changes to the page like adding
an image, carried in the page descriptor (its `left` and `top` edges and
its size) with the images' revision raised. Writing within 5 mm of an edge grows the page by
5 mm on that side, counted from the sheet's edge. An image is placed at its natural size, taken from
the resolution recorded in the file, or 0.1 mm per pixel when there is
none, centred across the sheet, 8 mm from the top or from the image
above, scaled down if wider than the sheet inside its margins (or, if the
person adding it chooses, placed at natural size with the sheet widened
to hold it). A page holds at most 12 images and 24 MB of image data.
none (the nominal 72 and 96 dpi that cameras and screenshots write count
as none), centred across the page, 5 mm from the top or from the image
above, the sheet widened equally on both sides if it is wider than the
page; only one wider than a page can be, 2 m, is scaled down. A page holds at most 12 images and 24 MB of image data.
An image file over 2400 pixels on a side or 1.5 MB, or not PNG or JPEG,
is redrawn at most 2400 pixels and stored as JPEG before it is shared;
smaller PNG and JPEG files are shared byte for byte.
A text block is free text, or a *text rectangle* with a width and a
height, drawn with a red frame on a white ground; the ground is drawn
beneath every layer, so that it hides an image and never anyone's
writing, and the frame within the owner's layer. A *Hide* rectangle is
a page object like an image, in the page descriptor and the page file,
drawn white above every layer with blue corners on screen; made, moved
and removed by the host and co-hosts. Both are moved by a hold of
the pen and resized from their corners, like images but with their
shape free; an image keeps its shape.
A stroke is a list of points with pressure, and the line width is a
function of pressure: nominal at a pressure of 0.4, about 2.6 times that
at 0.75. Points are sent while the stroke is drawn, so the others see it
@@ -127,7 +158,7 @@ it, rewritten a moment after every change (at most every 400 ms while
writing continues, at least every 2 s). A *kept* page is a copy of
`Current.svg` named `YYYY_MM_DD_NN.svg`, the day being when the page was
last changed and NN a serial number within that day. A copy is made when
the user asks (**Keep page**), and by the program whenever `Current.svg`
the user asks (**Keep**), and by the program whenever `Current.svg`
is about to be given to another page: when the session moves to a new
page, when the notebook is changed, and when a session begins and finds
a page of another session in the notebook. Nothing is copied if the
@@ -213,7 +244,7 @@ computer behind a home router is not without arrangement.
The client uses the browser's pointer events, which deliver every sample
a pen produces, with pressure, in Firefox and in Chromium-based browsers
on Linux and macOS. The panel's last line shows what arrives. When a
on Linux and macOS. The browser's console shows what arrives. When a
pen has been used, touches are ignored, so a hand resting on a touch
screen does not draw; this rule has not yet been tested with a device
that has both a pen and a touch sensor. Kokkoja opens none of the
@@ -221,6 +252,23 @@ browser's system dialogs (the colour palette and, with the local
program, the file chooser are its own), because one such dialog crashed
Firefox on Linux.
## Adjustable choices
Some choices of the interface are named constants at the top of the
file that uses them, so that they can be changed in one place, or offered
as preferences later: the line widths and the five colours (`main.js`),
the zoom step of the wheel and the zoom per 100 pixels of a pen drag
(`renderer.js`), the dead zone of that drag (`pen_input.js`), the margin
by which the page grows (`page_model.js`), the tooltip delay
(`tooltip.js`), the hold that moves an image (600 ms, with 6 pixels of
slack, `pen_input.js`), the reach of an image's corner (24 pixels,
`renderer.js`), the smallest an image can be scaled to (1 cm,
`page_model.js`), how often a dragged image's place is sent (40 ms,
`session.js`) and the points of a stroke (25 ms, `session.js`), the colour and width of a text rectangle's frame
(`page_file.js`) and whether the frame is shown whole or as corners on
screen (`TEXT_FRAME_STYLE` in `renderer.js`; the file and the export
always hold the whole frame).
## Development
The code is Python and JavaScript only. The tests run with
+50 -5
View File
@@ -33,7 +33,7 @@ layer of its own. You can erase your own writing and nothing else; nobody
else can erase yours. Any layer can be hidden on your own screen without
affecting anyone else's view.
**A page that grows.** A page is an A4 sheet. When writing approaches an
**A page that grows.** A page is an A4 sheet, lying on its side. When writing approaches an
edge, the sheet grows in that direction, so you are never short of room.
An image is placed at its natural size; one wider than the sheet is
scaled down to fit, and the sheet grows downward to hold further images.
@@ -62,8 +62,9 @@ host runs Kokkoja.
**Free software.** Kokkoja is free and open-source. Anyone can use it
without paying, and nobody depends on a company's service. It runs in
Firefox and in browsers built on the Chromium engine, such as Brave and
Chrome, on Linux and macOS.
Firefox, in browsers built on the Chromium engine, such as Brave or
Chrome, and in Safari, on Linux and macOS; a guest can also join from
Safari on an iPad or an iPhone, writing with a pencil or a finger.
## Who uses it
@@ -93,8 +94,52 @@ A session has one *host*, the person who starts it, and any number of
guests. The host sends the guests an invitation, by email, in a chat
message, or by any other means. The host *drives*: only the host puts
images on the page, starts new pages or loads pages from a notebook, and
everyone follows. The host can give any guest the same abilities, for a
discussion among colleagues.
everyone follows. The host can make any guest a *co-host*, with the
same powers, for a discussion among colleagues.
## What runs where
Three kinds of machine take part, and it helps to have the picture in
mind before reading on.
```
Host's computer Server Guests
--------------- ------ ------
Kokkoja program kokkoja.andykopra.com Guest A: a browser
serves the client -+ | opens the link
to the browser at | +----+-----+ the server serves
localhost:8760 | | relay | <-------------> the client to it;
keeps the notebook | | (passes | notebook in the
as files | | messages,| browser, downloads
| | | reads |
browser <-----------+ | nothing) | <-------------> Guest B: has the
| +----------+ Kokkoja program too;
+---- encrypted ----------^ | joins from its own
messages | start screen; note-
web site: book as files
the client's files
```
* **The host** runs the small Kokkoja program on their own computer.
It shows Kokkoja in the host's browser, at the address
`http://localhost:8760/`, and it keeps the host's notebooks as
directories of files. It must be running for as long as the host is
in a session; the browser alone cannot reach the notebooks.
* **The server** does two things for everyone. Its *relay* passes the
participants' messages, which are encrypted before they leave each
computer, so the relay reads nothing. Its *web site* serves the
Kokkoja client to guests who join by link. The host's program is
told the server's address once, when it is started.
* **A guest** needs only a browser: opening the invitation link fetches
the client from the server's web site, and the guest's notebook lives
in that browser, with kept pages and exports offered as downloads. A
guest who wants a notebook of real files runs the Kokkoja program as
the host does, and joins from the program's own start screen.
Every participant's computer holds the whole page. Only changes travel,
through the relay, to everyone else. Nothing is stored on the server.
The rest of this documentation describes Kokkoja from the guest's point of
view first, since the host sees the same screen and has a few more