What Drafter can do
Goal
You are about to pick a final project, and you want it to be interesting. This page is a tour of the parts of Drafter that the guided projects did not show you: sound, cameras, maps, timers, charts, live typing, files, and more. Each part is short. Read the ones that sound fun, follow the links to try the live demos, and collect ideas as you go.
You do not need to understand every line of code here. The point is to know what is possible, so that when you plan your project you can say "Drafter can do that" and go find out how.
Before you start
Everything on this page uses the same three ideas you already know:
- A route is a function that returns a
Page. - State is a dataclass that remembers things between clicks.
- A component is a Python object, like
ButtonorTextBox, that becomes part of a page.
The new components below work the same way. Most of them simply hand your route a new kind of value: a photo, a location, a sound clip, or the number of seconds that passed. Your route decides what to do with it.
Every feature here has a full reference page with every parameter and a working demo. When a feature catches your eye, open its link. If you are using an AI assistant to help you program, paste that link into the chat so the assistant works from the real documentation. There is more advice about that near the end of this page.
Make sound without any sound files
Drafter can play notes straight from Python. No audio files are
needed. Tone plays one note, and Melody plays a list of notes
with a tempo. Both can use different waveforms, like a buzzy
"square" or a bright "sawtooth", and both accept effects like
Echo and Reverb.
from drafter import *
@route
def index() -> Page:
return Page([
Header("Victory Fanfare"),
Melody([("C4", 0.5), ("E4", 0.5), ("G4", 0.5), ("C5", 1.5)],
tempo=160),
"\nWith echo: ",
Melody([("C4", 0.5), ("E4", 0.5), ("G4", 0.5), ("C5", 1.5)],
tempo=160, effects=[Echo()]),
"\nDoorbell: ",
Tone("E5", duration=300)
])
start_server()
A melody is a plain Python list, so a loop can build one. That means your program can compose music from data: a song from the letters of a name, a tune that gets faster as the game gets harder, or a random melody generator.
Project sparks: a tiny piano, a song-guessing game, a "name to melody" converter, a Morse code trainer, a sound effects board for a story.
Look it up: Tone, Melody, Audio effects, Sound for playing files with effects, and Audio for background music that keeps playing across pages.
Record the visitor's voice
AudioRecorder shows a record button. The visitor records a short
clip, and your route receives it as a Recording. You can play it
back with Sound, and Sound can change the speed, add an echo, or
muffle it like a voice through a wall.
AudioRecorder("shout", max_duration=5000)
@route
def cave(state: State, shout: Recording) -> Page:
if shout.data_url is None:
return index(state)
return Page(state, [
"Chipmunk version: ", Sound(shout.data_url, speed=1.6),
"\nCavern version: ", Sound(shout.data_url, effects=[Reverb()])
])
The visitor can always refuse the microphone. When that happens the
recording arrives empty, and your code checks for None instead of
crashing.
Project sparks: a voice memo board, a "say the password" game, a pronunciation practice app, a sound-effect transformer.
Look it up: AudioRecorder and Audio types.
Use the camera and edit photos
Camera asks permission, shows a live preview, and sends the photo
to your route. The photo arrives as a Picture, which is a Python
value you can store in state, transform, and compare in tests.
Pictures come with transformations. Each one returns a new picture, so you can show the original and every version side by side.
@route
def develop(state: State, shot: Photo) -> Page:
if shot.picture is None:
return Page(state, ["No photo (" + shot.status + ")."])
return Page(state, [
shot.picture.resize(120, 90),
shot.picture.grayscale(),
shot.picture.flip_horizontal(),
shot.picture.rotate(90),
Download("Save it", "photo.png", shot.picture)
])
You can also work pixel by pixel. get_pixel(x, y) returns a color
and set_pixel(x, y, color) changes one, so a loop can invert
colors, draw a border, or hide a secret message in an image.
Picture.new(width, height, color) creates a blank picture from
nothing, which means your program can draw its own art.
Visitors can upload existing pictures with FileUpload, and
Download lets them keep the result.
Project sparks: a photo booth with filters, a pixel-art editor, a "spot the difference" generator, a profile picture maker, a meme captioner.
Look it up: Camera, Picture, Use pictures, and the photo editor example.
Put things on a real map
Map shows a real, scrollable world map. You can pin markers with
labels, and a click on the map calls one of your routes with the
latitude and longitude of the spot.
Map("spot", center=(39.68, -75.75), zoom=15,
markers=state.pins, on_click="add_pin")
@route
def add_pin(state: State, latitude: float, longitude: float) -> Page:
state.pins.append(MapMarker(latitude, longitude, "Seen here"))
return index(state)
CurrentLocation asks the visitor for their real position. If they
say yes, your route receives the coordinates. If they say no, the
value's status tells you, and the app keeps working.
The map pictures come from the internet, so the map needs a connection. Clicks still work without one.
Project sparks: a campus food map, a travel wish list, a "guess the city" game, a lost-and-found board, a running route logger, a scavenger hunt that checks how close you are.
Look it up: Map, CurrentLocation, and Location and map types.
React while the visitor types
So far your apps change only when a button is clicked. Any
component can also fire an event, like on_input for every
keystroke or on_change when a choice settles. The route that
answers can return a Fragment, which replaces one small part of
the page instead of the whole thing.
from drafter import *
@route
def index() -> Page:
return Page([
Header("Password Strength"),
TextBox("password", "", on_input="judge"),
"\n",
Output("verdict", ["Type a password."])
])
@route
def judge(password: str) -> Fragment:
if len(password) < 6:
verdict = "Too short."
elif password.lower() == password:
verdict = "Add a capital letter."
else:
verdict = "Strong enough for a cat."
return Fragment([verdict], target="#verdict")
start_server()
A route can also return Update, which changes state silently with
no visible reaction. That is how autosave works: every keystroke is
saved, and the visitor can wander off to another page and come back
to their draft.
Project sparks: a live word counter, a rhyme suggester, a unit converter that updates as you type, a search box that filters a list, a typing speed test.
Look it up: Add live behavior, Fragment, Update, and Keyword attributes for the full list of events.
Make things happen over time
Timer counts down and calls a route when it reaches zero. Clock
ticks forever and calls a route on every tick. Both measure in
milliseconds, so one second is 1000.
from drafter import *
from dataclasses import dataclass
@dataclass
class State:
hunger: int
@route
def index(state: State) -> Page:
return Page(state, [
Header("Tamagotchi Minute"),
Clock(2000, "hungrier", show=False),
Output("status", ["Captain is content."]),
"\n",
Button("Feed", "feed")
])
@route
def hungrier(state: State) -> Fragment:
state.hunger = state.hunger + 1
if state.hunger < 3:
mood = "Captain is content."
else:
mood = "Captain demands salmon. (" + str(state.hunger) + ")"
return Fragment([mood], target="#status")
@route
def feed(state: State) -> Page:
state.hunger = 0
return index(state)
start_server(State(0))
Normally a timer is part of the page that made it, so leaving the
page stops it. Add persistent=True and the countdown keeps running
while the visitor checks their inventory on another page.
Project sparks: a speed quiz, an idle clicker game, a pomodoro study timer, a slideshow that advances itself, a reaction-time test, a pet that gets hungry while you are away.
Look it up: Make things happen over time, Timer, and Clock.
Listen to the room
Microphone does not record anything. It measures how loud the room
is and calls a route when the sound crosses a threshold. It can also
draw a live volume meter, waveform, or bars.
Microphone("mic", threshold=0.4, on_loud="noise", visualize="bars")
@route
def noise(state: State, volume: float) -> Fragment:
if volume > state.record:
state.record = volume
return Fragment(["Loudest so far: " + str(round(state.record, 2))],
target="#result")
Project sparks: a clap-to-start game, a "shout meter" contest, a classroom noise warning, a game where silence ends your turn.
Look it up: Microphone.
Draw charts and pictures from data
Drafter apps run real Python, so the plotting library matplotlib
works. Draw a chart in your route, add MatPlotLibPlot() to the
page, and the chart redraws from state on every visit.
import matplotlib.pyplot as plt
@route
def index(state: State) -> Page:
plt.bar(range(len(state.walks)), state.walks)
plt.title("Blocks walked with Babbage")
return Page(state, [
Header("Walk Tracker"),
MatPlotLibPlot(),
Button("Log a long walk", "long_walk")
])
For shapes, SVG takes drawing instructions as text. A loop can
build a row of circles, a bar chart with your own colors, or a
simple game board. For pixel art, Picture.new and set_pixel work
too.
Project sparks: a mood tracker with a weekly chart, a budget app with a spending graph, a dice-roll probability demo, a generative art page where every visit draws something new.
Look it up: MatPlotLibPlot, SVG, and the images playground.
Move files in and out
Your app runs inside the browser tab, so it cannot read the
visitor's hard drive on its own. Two components move files across
that boundary. FileUpload lets the visitor choose a file, and your
route's type annotation decides what arrives: str for text,
bytes for anything, or Picture for an image. Download offers a
file your app built, such as a text log or a CSV that opens in a
spreadsheet.
FileUpload("poem_file", accept=".txt")
Download("Download the journal", "journal.txt", journal_text)
Your app can also read data files that you ship with it, using
plain open(). That is how you give a quiz app two hundred
questions without typing them into Python.
Project sparks: a word-frequency analyzer, a grade book that imports a CSV, a flashcard app that reads decks from text files, a journal with a "download my entries" button.
Look it up: Upload and download files and File types.
Collect dates, show progress, hide details
Some small components make apps feel finished with very little code.
DateInput,TimeInput, andDateTimeInputshow the browser's own calendar picker. Annotate the route parameter asdateand you receive a real Python date with.yearand.month.ProgressBar(answered, total)draws a bar that fills up.Meter(value, min=0, max=100, low=30, high=70, optimum=10)draws a gauge that changes color when the value leaves the healthy zone.Details("Hint", "The answer is...")is a collapsible section. Give several the samegroupand they become an accordion where opening one closes the others.Table(state.entries)turns a list of dataclasses into a table in one line.
Project sparks: a countdown to an event, a habit tracker with streak gauges, a study guide with hidden answers, a pet health dashboard.
Look it up: DateInput, ProgressBar, Meter, Details, and Table.
Change the whole look in one line
There are more than twenty themes. One call at the top of your program restyles every page:
set_website_style("terminal")
A few more one-liners: set_website_title("Snack Tracker") names
the browser tab, set_website_favicon("icon.png") gives it an icon,
and set_page_transition("fade") makes pages fade in. Every
component also accepts style_ keywords, like
Button("Go", "start", style_font_size="24px"), and you can add
your own CSS for anything else.
Look it up: the theme catalog, Change the appearance, and Site configuration.
Use other Python libraries
Because your app is real Python, import random works, and so do
many libraries from PyPI. When Drafter sees an import in your file,
it installs that library in the browser before your app runs. You
also need the library installed on your own computer, with pip or
your editor's package manager, so the first local run works.
Not every library can run in a browser. The scientific libraries have special browser builds, and any pure-Python package installs fine. Libraries that need a database, other processes, or network sockets cannot work, and no setting changes that. The quick test is to search Pyodide's package list.
Here are ten libraries that work in Drafter and that engineers tend to find useful. Each link goes to a beginner tutorial.
- NumPy is the foundation of scientific Python. It gives you arrays, so you can add two lists of a thousand measurements in one line, take averages, and do matrix math. Most of the other libraries here are built on it. Spark: a signal viewer that averages noisy sensor readings.
- pandas
reads a CSV file into a table and lets you sort, filter, group,
and summarize it. Pair it with
FileUploadand your app becomes a data explorer for any spreadsheet. Spark: a lap-time analyzer that reads a race log and reports the fastest lap per driver. - Matplotlib
draws line charts, bar charts, scatter plots, and histograms.
MatPlotLibPlot()puts the result on a page. Spark: a stress versus strain plot built from values the visitor types in. - SciPy adds the heavy numerical tools: solving equations, finding minimums, integrating, fitting curves to data, and filtering signals. Spark: a curve-fitting tool that finds the best line or exponential through uploaded data points.
- SymPy
does algebra with symbols instead of numbers. It can simplify
expressions, solve for
x, take derivatives and integrals, and print the result as nice math. Spark: a step-by-step calculus helper that shows the derivative of whatever the visitor enters. - Pint
handles physical units. Multiply
3 * ureg.meterby2 * ureg.secondand the result knows it is in meter-seconds, and converting to feet or miles per hour is one method call. Pint is pure Python, so it installs from PyPI the first time your app imports it. Spark: a unit converter that refuses to add meters to seconds and explains why. - NetworkX builds graphs: nodes connected by edges. It can find the shortest path, detect cycles, and measure which node is most central. Spark: a campus map that finds the shortest walk between two buildings, or a circuit checker that finds loops.
- Shapely
works with 2D geometry: points, lines, and polygons. It can
compute areas, test whether a point is inside a shape, and find
where two shapes overlap. Spark: a floor-plan tool that checks
whether furniture fits in a room, or a zone checker for
Mapclicks. - Pillow
is the standard image library. It can blur, sharpen, draw text
and shapes, and adjust brightness. Drafter's
Pictureconverts to and from a Pillow image withPicture.from_pil, so you can use Pillow for the stepsPicturedoes not cover. Spark: a photo booth that stamps a caption and a date onto each camera shot. - scikit-learn is the beginner-friendly machine learning library. Give it a table of examples and it can learn to predict a number or sort items into categories. Spark: a predictor that estimates a bridge's load limit from a few measurements, trained on a small dataset you bundle with the app.
The standard library counts too. random, statistics, math,
fractions, and datetime all work with no installation.
requests can fetch data from public web APIs, with one catch: the
other website has to allow browser requests. Public data services
often do. Ordinary websites usually do not, and nothing in your code
can change that. These five need no account or key, allow browser
requests, and return JSON that requests.get(url).json() turns into
Python values:
- Open-Meteo gives the current weather and a seven-day forecast for any latitude and longitude, plus air quality, wind, and solar radiation. Spark: a "should I bike today?" page, or a solar-panel output estimator.
- USGS Earthquake feed
lists every earthquake recorded in the last hour, day, or week,
with magnitude and location. Spark: drop each quake on a
Mapas a marker sized by magnitude. - Where the ISS at
reports the International Space Station's position, altitude,
and speed right now. Spark: a
Clockthat re-fetches every few seconds and moves a marker across aMap. - Frankfurter publishes daily currency exchange rates from the European Central Bank. Spark: a parts-cost calculator that converts supplier prices into dollars.
- Sunrise-Sunset returns
sunrise, sunset, and day length for a location and date. Spark:
combine it with
CurrentLocationto show how much daylight is left where the visitor is standing.
The answers come back as dictionaries, which your course may not have covered yet. Ask your instructor or your AI assistant to show you how to pull one value out, and look at the raw answer in the browser first so you know what you are looking for.
Heavy libraries make the first page load slower, because the browser has to download them once. Matplotlib is several megabytes, and scikit-learn and SciPy are larger. Warn your visitors on the first page, and only import what you use.
Project sparks: a dice game with real randomness, a data explorer for a public dataset you bundle with the app, a weather page for your town, a unit-aware engineering calculator.
Look it up: Third-party libraries.
Reach the browser directly
This one is for the adventurous. Inside a route, import js gives
your Python code the browser itself: the window size, the page
elements, and a Canvas you can draw on with the browser's own
drawing calls. A Page can also carry a small js or css string
to run when it renders.
@route
def inspect(state: State) -> Page:
import js
state.report = "Your window is " + str(js.window.innerWidth) + " wide."
return index(state)
Most projects that think they need this actually need a component they have not met yet. Check the component list first.
Look it up: JavaScript interop and Canvas.
Test it, freeze it, and ship it
These are not flashy, but they make a project feel professional and they save time near the deadline.
assert_stateandassert_hascheck a route's result in one line, and the debug panel shows which tests passed.- The debug panel's History tab records every page your app produced. You can copy a page you like as a ready-made test, so any later change that breaks it fails a test instead of surprising you.
Look it up: Test a feature and Freeze finished pages.
Combine two features
The most memorable projects mix two of the sections above. A few combinations to start your thinking:
| Idea | Features it combines |
|---|---|
| Photo booth with a countdown | Camera, Timer, Picture transforms, Download |
| Scavenger hunt | Map, CurrentLocation, ProgressBar |
| Rhythm game | Melody, Clock, Fragment |
| Voice-memo diary | AudioRecorder, DateInput, Table, Download |
| Study timer with a chart | Timer, MatPlotLibPlot, Meter |
| Clap-controlled slideshow | Microphone, Clock, Image |
| Mood tracker | DateInput, Table, MatPlotLibPlot, a theme |
| Pixel-art studio | Picture.new, set_pixel, Download |
Pick one feature as the heart of the project and build it first. Add the second feature only after the first works end to end. Choose a manageable idea explains how to keep the scope under control.
What Drafter cannot do
Knowing the limits early saves you from a dead end halfway through. All of these come from one fact: your whole app runs inside the visitor's browser tab.
- No real accounts or passwords. Everything is visible to the visitor, so a login can shape the flow but protects nothing. A clearly labeled login simulation is fine.
- No multiplayer or shared data. Each visitor runs their own copy. Two people cannot see the same chat or leaderboard. "Pass the laptop" turn-taking works well instead.
The project ideas that teachers see lists the same limits from the other side of the desk.
Working with an AI assistant
Your course allows you to use an AI assistant while you program. Used well, it makes the features on this page reachable in an afternoon. A few habits make the difference:
- Give it the real documentation. Paste the link to the reference page for the component you want. Assistants sometimes invent parameters or components that do not exist. The reference page is the truth.
- Ask for one small step at a time. "Add a Camera to the index page and show the photo on a second page" works better than "build my whole photo app".
- Paste the exact error. The debug panel and the terminal show the full message. Copy all of it into the chat.
- Ask it to explain the code it wrote, line by line, until you could rewrite it yourself. You will be the one presenting the project.
- Remind it where the app runs. If it suggests a database, a Flask server, sockets, or threads, that advice is for a different kind of program. Say "this is a Drafter app that runs in the browser" and it will usually correct course.
- Keep your tests. Ask the assistant to write an
assert_stateorassert_hastest for each feature it adds, and run them before moving on.
Common problems
- The feature works on your laptop but not on your phone. The
camera, microphone, and location all need permission, and some
phone browsers are stricter. Your route should handle a refusal
(check the
statusor aNonevalue) rather than assume a yes. - The sound does not play on the first page. Browsers refuse to play audio before the visitor has clicked something. Put the sound behind a "Start" button.
- The timer restarts every time you click. Each click rebuilds
the page, including the timer. Answer with a
Fragmentor mark the timerpersistent=True. - The map is blank. The map images need an internet connection. Clicks and markers still work.
- An AI assistant gave you code with
Flask,sqlite3, orthreading. Those do not run in the browser. Ask again with the link to this page.
You are ready for the next step when
You can name two features from this page that you want in your project, and you can say which one you will build first. Then go choose a manageable idea.