Touying (投影 in Chinese, /tóuyǐng/, meaning projection) is a user-friendly, powerful, and efficient package for creating presentation slides in Typst.
If you like it, consider giving a star ⭐ on GitHub. Touying is a community-driven project — feel free to suggest ideas and contribute!
- Beautiful themes — built-in themes like Simple, Metropolis, Dewdrop, University, Aqua, Stargazer and diverse themes on Typst Universe
- Fast — Typst compiles in milliseconds. Live previews update as you type, giving you the instant feedback.
- Rich animations —
#pause,#meanwhile, math equation animations, CeTZ & Fletcher support - Heading-based slides — write presentations like a document, no boilerplate
- Speaker notes — dual-screen support via tools like PowerPoint, HTML or pympress
- Export — Builtin PDF export, PPTX and HTML via touying-exporter
- Correct bookmarks — proper PDF outline and page numbers out of the box
- Full documentation and references (English & Chinese)
- Ask DeepWiki or Ask Zread for AI-assisted help
- Gallery — slides made by the community
- Universe — Diverse touying themes on Typst Universe
- Share slides instantly on GitHub with gistd or export slides to PPTX and HTML formats and show presentation online.
Make sure you have Typst installed, or use the Web App / Tinymist for VS Code.
#import "@preview/touying:0.7.1": *
#import themes.simple: *
#show: simple-theme.with(aspect-ratio: "16-9")
= Title
== First Slide
Hello, Touying!
#pause
Hello, Typst!Congratulations on creating your first Touying slide! 🎉
Touying supports incremental reveal with #pause and #meanwhile, math equation animations, and integrations with CeTZ and Fletcher:
| Math equations | CeTZ & Fletcher |
|---|---|
![]() |
![]() |
For the full feature set — cover mode, callback-style animations, #uncover, #only, #alternatives — see the documentation.
For a comprehensive example showcasing university theme, theorems, CeTZ/Fletcher animations, speaker notes, and more. You can also use the #slide[..] format to access more powerful features provided by Touying.
#import "@preview/touying:0.7.1": *
#import themes.university: *
#import "@preview/cetz:0.4.2"
#import "@preview/fletcher:0.5.8" as fletcher: node, edge
#import "@preview/numbly:0.1.0": numbly
#import "@preview/theorion:0.5.0": *
#import cosmos.clouds: *
#show: show-theorion
// cetz and fletcher bindings for touying
#let cetz-canvas = touying-reducer.with(reduce: cetz.canvas, cover: cetz.draw.hide.with(bounds: true))
#let fletcher-diagram = touying-reducer.with(reduce: fletcher.diagram, cover: fletcher.hide)
#show: university-theme.with(
aspect-ratio: "16-9",
// align: horizon,
// config-common(handout: true),
config-common(frozen-counters: (theorem-counter,)), // freeze theorem counter for animation
config-info(
title: [Title],
subtitle: [Subtitle],
author: [Authors],
date: datetime.today(),
institution: [Institution],
logo: emoji.school,
),
)
#set heading(numbering: numbly("{1}.", default: "1.1"))
#title-slide()
== Outline <touying:hidden>
#components.adaptive-columns(outline(title: none, indent: 1em))
= Animation
== Simple Animation
We can use `#pause` to #pause display something later.
#pause
Just like this.
#meanwhile
Meanwhile, #pause we can also use `#meanwhile` to #pause display other content synchronously.
#speaker-note[
+ This is a speaker note.
+ You won't see it unless you use `config-common(show-notes-on-second-screen: right)`
]
== Complex Animation
At subslide #touying-fn-wrapper((self: none) => str(self.subslide)), we can
use #uncover("2-")[`#uncover` function] for reserving space,
use #only("2-")[`#only` function] for not reserving space,
#alternatives[call `#only` multiple times \u{2717}][use `#alternatives` function #sym.checkmark] for choosing one of the alternatives.
== Callback Style Animation
#slide(
repeat: 3,
self => [
#let (uncover, only, alternatives) = utils.methods(self)
At subslide #self.subslide, we can
use #uncover("2-")[`#uncover` function] for reserving space,
use #only("2-")[`#only` function] for not reserving space,
#alternatives[call `#only` multiple times \u{2717}][use `#alternatives` function #sym.checkmark] for choosing one of the alternatives.
],
)
== Math Equation Animation
Equation with `pause`:
$
f(x) &= pause x^2 + 2x + 1 \
&= pause (x + 1)^2 \
$
#meanwhile
Here, #pause we have the expression of $f(x)$.
#pause
By factorizing, we can obtain this result.
== CeTZ Animation
CeTZ Animation in Touying:
#cetz-canvas({
import cetz.draw: *
rect((0, 0), (5, 5))
(pause,)
rect((0, 0), (1, 1))
rect((1, 1), (2, 2))
rect((2, 2), (3, 3))
(pause,)
line((0, 0), (2.5, 2.5), name: "line")
})
== Fletcher Animation
Fletcher Animation in Touying:
#fletcher-diagram(
node-stroke: .1em,
node-fill: gradient.radial(blue.lighten(80%), blue, center: (30%, 20%), radius: 80%),
spacing: 4em,
edge((-1, 0), "r", "-|>", `open(path)`, label-pos: 0, label-side: center),
node((0, 0), `reading`, radius: 2em),
edge((0, 0), (0, 0), `read()`, "--|>", bend: 130deg),
pause,
edge(`read()`, "-|>"),
node((1, 0), `eof`, radius: 2em),
pause,
edge(`close()`, "-|>"),
node((2, 0), `closed`, radius: 2em, extrude: (-2.5, 0)),
edge((0, 0), (2, 0), `close()`, "-|>", bend: -40deg),
)
= Theorems
== Prime numbers
#definition[
A natural number is called a #highlight[_prime number_] if it is greater
than 1 and cannot be written as the product of two smaller natural numbers.
]
#example[
The numbers $2$, $3$, and $17$ are prime.
@cor_largest_prime shows that this list is not exhaustive!
]
#theorem(title: "Euclid")[
There are infinitely many primes.
]
#pagebreak(weak: true)
#proof[
Suppose to the contrary that $p_1, p_2, dots, p_n$ is a finite enumeration
of all primes. Set $P = p_1 p_2 dots p_n$. Since $P + 1$ is not in our list,
it cannot be prime. Thus, some prime factor $p_j$ divides $P + 1$. Since
$p_j$ also divides $P$, it must divide the difference $(P + 1) - P = 1$, a
contradiction.
]
#corollary[
There is no largest prime number.
] <cor_largest_prime>
#corollary[
There are infinitely many composite numbers.
]
#theorem[
There are arbitrarily long stretches of composite numbers.
]
#proof[
For any $n > 2$, consider $
n! + 2, quad n! + 3, quad ..., quad n! + n
$
]
= Others
== Multiple columns
#cols[
First column.
][
Second column.
]
== Multiple columns with equal height blocks
#cols(columns: (1fr, 1fr), gutter: 1em)[
#emph-block[
First column with equal height: #lorem(10)
#lazy-v(1fr)
]
][
#emph-block[
Second column with equal height: : #lorem(15)
#lazy-v(1fr)
]
]
== Multiple Pages
#lorem(200)
#show: appendix
= Appendix
== Appendix
Please pay attention to the current slide number.Thanks to...
- @andreasKroepelin for the
polyluxpackage - @zral0kh for the waypoint feature and many improvements
- @enklht for many fixes and improvements
- @Enivex for the
metropolistheme - @drupol for the
universitytheme - @pride7 for the
aquatheme - @Coekjan and @QuadnucYard for the
stargazertheme - @ntjess for contributing to
fit-to-height,fit-to-widthandcover-with-rect





