Download the single file below and open it in any modern browser — it's the document, the viewer and the editor in one. Shipped files self-update through the signed release channel.
- A deck opens inside Teams and SharePoint again. Their viewer refuses
the way 1.1.0's file started itself (a script loaded from ablob:URL).
The file now starts the way nothing refuses: the runtime is inserted as an
inline script first, and only if a policy turns that down does it fall
back tonew Function, then to the blob import — measured in Teams with
seven variants. Inside such a viewer the frame has no storage, so
autosave and preferences do not persist there, and its policy blocks every
connection — so inside an embedded view the app makes no request at all:
no update check at launch, no language-pack listing, no live-session
socket; the About dialog says so. The deck itself opens, presents and
saves. - Maths is Bento's own now, and it reads Typst. Formulas in text used to
go through Temml, a 64 KB library that every saved deck carried. A small
engine of our own (slides/src/maths, about 8 KB) renders them instead, so
every file you save is about 78 KB smaller, and nothing in the file changes:
a formula is still the$…$source you typed. Measured against Temml on 91
formulas, from our own decks and from Temml's own list of supported
functions: 97.8% render pixel-identical, and the rest are places where
Temml drew nothing on Chrome —\overlineand\underlinenow draw their
rule. New: Typst maths, asked for by thimotedupuch (#358) — write
$typst: a/b$(or$$typst: …$$) and the formula is read as Typst:
sqrt(x),sum_(i=1)^n,mat(a, b; c, d),cases(…),"if" x. A plain
$…$is LaTeX as before. Not covered, for now:\substack,\xrightarrow,
chemistry (\ce),\tagand\hline; a formula using them shows as
typed, the way any TeX Temml refused always has. - Slides export as images. Save ▾ Export slides as images… writes
this slide, or every slide in the show, as PNG or JPEG at 1× or 2× — one
file per page, named after the deck (My_Deck-page-01.png), hidden slides
and interactive states left out the way a PDF leaves them out. Chromium
asks for a folder and writes the pages into it; other browsers get one
download per page; Safari can export the current slide. The picture is the
deck's own render — its fonts, gradients and shapes — with charts and media
as stills and web-linked images blank, since a file cannot fetch. No ZIP,
no second renderer: about four kilobytes of runtime. Asked for by den-sv
(#243, #261); the shape follows lazyeo's #306, kept to the thin half —
the heavier converter belongs to bento/convert. - A Layers list. The Slide panel now opens with Layers: every element
on the slide, top of the stack first, with a glyph and a short label (the
text's first words, or the kind). Click a row to select, shift-click to add,
drag a row to move it up or down the stack, or use ⌘↑ and ⌘↓ with the list
focused. With an element selected the same list closes its panel, so the
highlighted row is never far. Nothing new in the file: the list is a view
onto the order the four Order buttons (front, forward, backward, back) have
moved elements through since 1.0, and a row dropped somewhere lands exactly
where those buttons would put it. Asked for by Li Wei in discussion #371. - A deck can be written the short way. An AI agent writing a deck used
to spend most of its output on fields nobody chose — rotation 0, opacity 1,
the font stack, weight 400, centre, middle, line height 1.25, on every
element. A document marked"compact": truemay leave all of that out and
gets it back on load, filled from the same defaults the editor uses when
you insert an element;elementsmay nest arrays, and an element without
an id gets one minted from its slide and position, the same every time.
Save ▾ Copy compact JSON (for agents) andwindow.bento.compact()hand
a deck back in that shape; Replace from JSON… andloadDoctake it. The
saved file is unchanged — always full, so nothing older is affected.
Measured: 12–14% off a designed deck's JSON, about 3× off a deck written
the compact way. Asked for, with a working proof of concept, by
benedictjohannes (#411, #422); the nested arrays and the flattening come
from that proof. - The short way, round two: text sizes itself, markdown is accepted, and
a load says what it dropped. In a compact document a text element may
leavehout (or say"auto"): the box is sized to its text on load,
with the deck's real fonts — the same measurement as Fit height to text.
A text element may carrymdinstead ofhtmland it converts exactly as
pasted markdown does (bold, italic, code, strike, bullets and sub-bullets,
links). Andwindow.bento.loadDocnow returns a report: every key the
safety check discarded, with its path and the reason (/slides/0/elements/2/fontSze: unknown key), how many fields were filled in, andvalidate()'s findings
— so an agent's loop is load, read, fix, load again, instead of guessing
why a field vanished. Replace from JSON… summarises the same report in a
toast and logs it. Three agent-written decks are checked in and load clean
in CI. - An agent can place a slide by layout and role. In the compact form a
slide may say"layout": "title-body"and its elements carry arole
(title,body,subtitle,kicker,quote,attribution,image,
card1…) instead of coordinates and typography: the layout's frames and
type are used, the same way Apply layout fills a slide in the editor, and
slides born from the same layout still morph their chrome. Severalbody
paragraphs stack into the slot, each sized to its text; an element that
carries its ownx y w his placed as given. Four new built-in layouts —
Three cards, Quote, Image left, Image right — appear in the layout
picker for everyone, next to the five that were there. The file on disk is
unchanged: the layout is applied on load, and what is saved is the placed
slide. bento check: an agent can look at what it wrote.node scripts/bento-check.mjs deck.bento.htmlloads the deck in headless Chrome
and prints what the editor would otherwise keep to itself — text that
overflows its box (and by how many pixels), elements off the canvas, dead
links, effects that can never run — by slide, with element ids;--png out/
adds one PNG per slide through the same render path as Export slides as
images, and a contact sheet of the whole deck in one picture;--jsonfor
scripts,--fail-on warningfor a strict exit code. A document JSON works
as input too, checked inside the built shell. The other half of the agent
loop thatAGENTS.mddescribes: write, check, fix, check again.- The format has a schema, and every file says where it is. A JSON
Schema for the bento/slides document is generated from the same tables the
app uses to check what it loads, so it cannot describe a deck the app would
refuse. It is published athttps://bento.page/schema/slides.json(and a
version-pinned copy beside it), returned bywindow.bento.schema()in a
running file, listed inhttps://bento.page/llms.txtfor AI agents, and
named in the Tooling comment at the top of every deck. A deck that carries
"$schema"at the top validates in any schema-aware editor; the app ignores
the key. Runtime cost: about 2.6 KB in the shell. - A saved deck names its schema. The first key of the saved JSON is now
"$schema": "https://bento.page/schema/slides.json"— 50 bytes, so a
reader with only the file in hand knows the format. Older versions keep the
key and write it back unchanged; nothing fetches it. - Connectors for diagrams. Three asks from xairy, in one go. A
Curved connector (#302): a curve that sticks to the elements at its ends
and re-routes when they move, like the straight Connector, and carries a
tip that points the way the curve arrives rather than along the chord; any
open curved line can take tips now. Seven more tip styles (#303): open
arrow, triangle, hollow triangle, diamond, hollow diamond, square, hollow
circle — a hollow head stops the line at its back edge, so nothing shows
through. A Double arrow shape (#304): the solid arrow with a head at both
ends; and a line has always taken an arrowhead at each end through its
start and end tips. Sticking (#301) has been in since 1.0.2. Old decks are
untouched: the original tips keep their exact geometry. A deck that uses
the new tips opens in 1.1.0 and older, but those shells draw a bar where a
new tip should be; a double arrow shows there as a single one. - A picture can be moved and zoomed inside its frame. Double-click an
image and the frame becomes a window onto the whole picture: drag to choose
which part shows, scroll or pinch to zoom in, Enter to keep it, Esc to put
it back. The Crop section of the image panel has the zoom as a number and a
way back to the whole picture. The crop is one small optional field on the
image; a deck opened in an older version shows the cover-fitted picture,
never a blank frame. Canvas, thumbnails, the show, print and file-manager
previews all show the same crop, and a morph between two cropped copies of
a picture glides between them. Asked for in discussion #319 by morreau. - A pasted photo no longer costs megabytes. Insert or paste a picture
and it is stored at slide resolution — the long edge capped at 2560 px,
crisp on a 4K projector — and photos are re-encoded as JPEG, so a phone
photo adds a few hundred KB to the file instead of three or four MB.
Screenshots, logos, charts and anything with transparency are only
downscaled, never made lossy, so text in them stays sharp; SVG and GIF are
left alone; and when re-encoding would not save at least a fifth, the
original bytes are kept. When the saving is worth mentioning a toast says
so ("Photo stored at 2560 px — 3.8 MB → 410 KB"). The picture panel shows
what is stored and offers Replace file (original size)… for the times
you want every pixel; Shrink photos on insert in the About dialog turns
it off for this browser. Nothing in an existing deck changes until you
insert something new. The file itself is untouched: a picture is still a
picture. - An image can drop its aspect ratio, and the properties panel stays
where you left it. Contributed by 1eevy (#372). Images gain a Keep
aspect ratio switch in the Fit & corners section, on by default: turn it
off and width and height resize independently (the image stretches to
fill); Shift is the one-drag exception in either direction, as it always
was. Turning it back on keeps the shape the image has at that moment. And
changing a control near the bottom of the properties panel no longer throws
the panel back to the top. The same PR proposed editable template pages
with locked furniture and a save-time asset compactor; the compactor's job
landed as #447/#476, and locked furniture is a design question for a
discussion rather than a change to how layouts work. - A date can pin its format, and fields are one click away.
{{date}}
and{{time}}have resolved in text since 0.9.12, but they followed the
viewer's locale — an author could not say M/D/YY and have every viewer see
it — and nothing in the editor said the tokens existed. Now
{{date:M/D/YY}},{{date:D MMMM YYYY}},{{time:h:mm a}}pin the
shape (YYYY YY MMMM MMM MM M DD D HH H hh h mm ss A a; a word in
[brackets]stays literal; month names still come in the viewer's
language), and the Text panel gains a Field picker that drops a page
number, date, time, title or document property into the text — the date
and time entries show today in each shape so the choice is made by eye.
Bare{{date}}is unchanged. Asked for in discussion #381 by Jef Ducon. - Every file is about 38 KB smaller. The runtime's two compressed blocks
used to be base64; they are now base86 — 86 printable characters chosen so
the text can never close or comment out the block that carries it — which
is 6.25% denser (4 bytes in 5 characters instead of 3 in 4). Measured on
the release shell: 699,847 → 661,768 bytes. Older versions keep opening
their own files; this one still reads theirs. The new decoder is also
quicker than the oldatobpath (11 ms against 47 for the runtime). - A deck opened in a background tab is ready when you switch to it. The
compressed file used to finish starting in a later task and hold its
splash on a timer — in a tab that was not visible (or a viewer rendering
the file off-screen for a preview card) timers are throttled and frames
never come, so the editor sat behind the splash until the tab was looked
at. The runtime now unpacks and starts inside the file's own script,
before the page is even "loaded", and a hidden document drops the splash
the moment the editor exists; visible, the brand moment is held for at
most 0.8 s and never waits on its own fade. - A web link whose address contains a dollar sign works again. Since
links arrived, an address like…/$a$bhad its two dollars read as a
formula and the link broke; formulas are now looked for in the text only,
never inside a tag. - A pasted code snippet keeps its code. The table the app uses to know
an element's fields had no entry for the code element, so a pasted or loaded
code block kept its box but lost its content, grammar and theme — an empty
snippet — andvalidate()did not know its fields. Found while building
the schema from that table; fixed. - A plain Save drops unused images too. 1.1.0 promised that a save
leaves out every image nothing in the deck refers to, and it did — on
every path except the one most people use. ⌘S and the Save button write
through a different route, and the clean-up never ran there, so a deleted
screenshot stayed in the file. It runs on every way of saving now.
Reported again, with a step-by-step, by charlycoste (#442, fixed in #476). - Every language pack is complete. The 22 downloadable packs had fallen
to 91% of the interface — the formatting bar, the context menu, the canvas
help, hidden slides, appearance, and the whole live-broadcast surface
showed in English. All 22 carry every string again. - One brand yellow. Ten places in the editor chrome — the speaker view's
timer, buttons and current thumbnail, the show's link, selection and
progress colours, the follow chip, and the path editor's anchor dots —
carried their own copy of the accent instead of reading the chrome's
--accenttoken. They read the token now; nothing looks different. The
slide-list highlight already did. The deck's owntheme.accentis a
separate thing and stays separate: chrome does not recolour per deck.