style guide

how phy makes things: the paper, the pencil, the words and the fwiends.

This is phy's house style: one voice and one look for everything phy makes, from pages, slides and posters to READMEs, hobby projects and code. Hand it to anyone, or any agent, making something for phy. A paper's text has its own guide; from a paper, this one covers only the main diagram (§8).

The idea behind all of it is to work in pencil: graphite on paper, drawn rather than designed, and as easy to erase and redraw as a pencil line. Colour belongs only to what the work is about.

This guide is the standard. Where anything else disagrees with it, the guide wins; where the guide is wrong, it is corrected here. The source is STYLE.md in rareone0602/phy_friends, also at its raw address for an agent to read. Other projects copy the kit (§7) from the same commit and point to the source; a copy does not update itself. §2 is the guide in short, for a brief with no room for the rest.


1. who phy is

2. principles

  1. Work in pencil. Keep the source (a spec, a script, a Markdown file) and the command that builds from it. Each thing has one folder, with its examples beside it; working output goes in a git-ignored out/, and a built file is committed only where publishing needs it, never edited by hand. When something goes, all of it goes: no leftover references, no half-renamed words, no commented-out code; git remembers. Candidates that lose are set aside in a backup/ folder until phy says otherwise. A first version is a sketch, to be redrawn once phy has looked.
  2. Few rules (low Kolmogorov complexity). Derive values rather than pick them: shades from colours, every vertical measure from the rule spacing. When matching something, name the one reference and what is matched, and measure the difference at the size people will see. If special cases pile up, the rule is wrong.
  3. Don't make it too artificial. Drawn, not designed: honest and quiet, with lines that wobble and spacing a little uneven (§6). No gradients, glows, glass, drop shadows, emoji, stock icons, hero copy or template layouts such as a grid of cards.
  4. Graphite on paper; colour for the subject. Everything is in paper and ink tones (§4), graphite rather than black, except what the work is about: the characters on a page, the data in a figure, the things and places in a film. Red is only for an error.
  5. One layer. Colour goes on in one layer: flat in a chart, in coloured pencil on a character or a thing (§9). Each colour has at most one shade, the house step darker: CIELAB L* −10 and chroma ×1.2 at the same hue, with a near-grey leaning to lavender, as shadeOf in src/phyfriends.js computes it. The shade only parts two neighbours of the same colour, such as a character's body under its head. No highlights or second steps, and nothing copied from a reference's light and shade. The pencil's texture is paper, a scale of data values (§8) is data, and light (§12) is a shape drawn on the place, never on what stands in it: none of them is shading.
  6. Keep the set aligned. Within a set (a family of characters, a figure series, a deck), anything new matches what is there in look, size and structure, and in code where the set is code. When a rule changes, it changes everywhere at once.
  7. Tell things apart first. If two parts can't be told apart, fix that before matching a reference any closer. Simplify only down to what makes a thing itself, never past it.
  8. The owner decides. People and their characters appear only with their owners' say-so, and as the owners give them: the name in the owner's capitalisation, the species, colours and pronoun, and a credit linking to the owner. An owner's wish beats a house rule.
  9. Make it cute. Characters are soft and round, with a big head on a small body. Cute comes before literal, but never at the cost of principle 7.
  10. Not the mode. The most likely choice (the template, the stock phrase, the default chart, the formal tone put on for show) is the boring one; look for the way that fits this idea. phy's work stays consistent through its rules, not through sameness: inside them each piece is a fresh draw, as the face's letters are, though principle 6 still holds within a set. A piece made to delight should surprise, and then, on a second look, seem the only way it could have gone.

And the particulars:

Fixed rules and defaults. A few things hold in everything phy makes: the owners' say (principle 8), pencil on paper, the characters as their specs draw them (§9), the one face (§5), the words in prose and in code (§3, §10), private things (§11) and whose music a film may use (§12). The rest of the look, from the ruled paper and the layout to the marks, colour, light and motion, is the house setting. A page, a chart or a deck keeps it, since there sameness is what makes it easy to use. A piece made to delight (a picture, a film, a game) may depart from whatever the idea needs, and says so: a line at the top of its source names each departure and why, and a series departs together (principle 6).

3. words

The model is The Economist: opinionated, analytical and dry. Words are few, exact and honest, like a note in the margin.

dodon't
that's meMeet phy, the creator behind it all!
nothing saved yet.Oops! Looks like there's nothing here yet!
the second method is better: it halves the build time.Both approaches have their merits.
one script redraws every figure in about 40 secondsBlazing-fast, seamless figure generation

4. paper and ink

The page is a sheet of lined notebook paper: pale blue rules, a faded red margin line on the left, and a little paper tooth. Everything is written on the rules: every vertical measure is a whole number of rules, and text sits on them. The values are tokens in site/notebook.css, on the paper and on the dark sheet:

tokenpaperdarkuse it for
--paper#fbf9f3#3d3c39the sheet
--rule#cfdbe8#45505athe pale blue rules, --L apart: 32px, 30px on phones, with --band, 4 blank rules, above the first
--margin#e6aaa3#8b5751the faded red margin line, --margin-x in: 88px, 22px on phones
--ink#3d3c39#fbf9f3graphite, 10.5:1: anything, titles, names and body text
--ink-2#6d6a63#b4b1a95.1:1: quieter words (intros, small print, notes, captions, table heads, code), the lines of controls, focus
--ink-3#97938a#86837a2.9:1: marks only (dashes, squiggles, the quote line, disabled controls); never words someone needs to read
--red#a64a3f#fe9c8c5.4:1: errors, and nothing else; the margin's red, pressed harder

5. type

One hand on one dial. Everything is written in one face, Shantell Sans: a hand drawn with one even line, like a fine pen held lightly. The pencil is in the rest: the ink, the paper, the marks and the filter over the title. The face's informality axis lets the same hand be neat in a diagram and loose in a toy, so formality is a setting of the one voice, never another voice; above the neat end, a letter that repeats comes out a little differently each time. Only code (the system monospace) and words in a host that sets its own type (a paper's text and maths, a README on GitHub) are in anything else.

The dial. Each work has one number, --informal, set by what the work is, not where it appears: 0 for a paper's diagram and for charts (matplotlib can't turn it), 50 for a site, notes, slides and posters, and 100 for a hobby project, its site included. A work may sit anywhere between; these are the usual stops.

The print and the hand. Running words, the print, sit at the work's number d (INFM d) and never bounce. The few words written rather than set, the hand (titles, headings, notes, captions, table heads and names), are never neater than 50 and bounce in the top half: INFM max(d, 50), BNCE max(d − 50, 0). A chart's words are all at 0 (§8).

Weight is pressure. Everything is Light (300); bold is 600, the pencil pressed harder. There are no other weights and no italics, not even for the title of a book.

Sizes step by 1.2 from the 18px print. On a phone the hand and the intro step down one (in brackets); the rest of the print keeps its size.

in the handsizerules
title64px (54px)3 (2)
part title54px (45px)3 (2)
h237px (31px)2
h3, a result26px (22px)1
note, caption, table head22px (18px)1
in the printsizerules
intro22px (18px)1 (2)
body18px1
small print, code15px1

6. hand-drawn marks

Every mark is a single pencil stroke: an SVG path with round caps, 1.1–1.5px wide, in an ink, never filled. A page has nine:

If a page needs a tenth mark, write a word instead; a piece (§12) draws whatever it needs, rain or speed lines, by the rule above. A mark may turn to point somewhere or grow to hold its own; it is still the same mark.

<svg width="0" height="0" style="position:absolute" aria-hidden="true">
  <filter id="graphite" x="-2%" y="-20%" width="104%" height="140%">
    <feTurbulence type="fractalNoise" baseFrequency="1.1" numOctaves="2" seed="7" result="n"/>
    <feColorMatrix in="n" type="matrix" values="0 0 0 0 0  0 0 0 0 0  0 0 0 0 0  0 0 0 -2.4 2.1" result="grain"/>
    <feComposite in="SourceGraphic" in2="grain" operator="in" result="g"/>
    <feTurbulence type="fractalNoise" baseFrequency="0.04" numOctaves="1" seed="2" result="w"/>
    <feDisplacementMap in="g" in2="w" scale="1.5" xChannelSelector="R" yChannelSelector="G"/>
  </filter>
</svg>

7. parts of a page

The kit, to copy from rareone0602/phy_friends: site/notebook.css (the paper), site/pencil.css (everything drawn on it; its opening comment lists the classes), site/icon.svg (the favicon), figures/ (§8) and the graphite filter (§6). specimen.html shows every part below at work, on the rules, with the dial to turn. A page is put together as the specimen is: <html lang="en-GB"> with the colour-scheme meta (§4); in the head the face, then notebook.css, then pencil.css, and --informal on :root where the work is not at 50; the graphite filter first in the body; and a main holding a header (the title, the intro and any nav), the writing inside .doc, and a footer.

Layout. One column, 42em at most, left-aligned against the margin line: a title in the hand, one line of intro, the content and a small footer. Nothing else is required.

Small print, in --ink-2: the nav, a date line, a contents line and the footer.

Links are the text itself, underlined like a pencil line (1px, 3px below), darkening to --ink when hovered.

Lists, quotes and code. A list item starts with a dash in --ink-3, and a numbered one with its number in the hand. A quote is indented, in --ink-2, with the side line. Code is one line to a rule; a block is indented like a list, with no box, and wraps rather than scrolls.

Pictures are a whole number of rules tall, with top and bottom on rules, no frame, no rounded corners and no shadow, and are scaled to fit, never cut: a chart loses nothing on a phone. A photo stays a photo. A scan or a line drawing on white is laid in with multiply, so its white becomes the paper, and keeps its white on the dark sheet (§4); a character or a chart is drawn on paper or on nothing. The caption is a note on the next rule. Never put words over a photo.

Tables. The rules are the rows: no borders, stripes or vertical lines. The head is in the hand in --ink-2, with the rule under it gone over in pencil, and a cell that wraps takes whole rules. A phone fits three columns: fold the rest into a label, or split the table.

Controls:

States:

A portrait. An about page may have one character standing beside the title, a rule taller than it. It stands on the title's rule, reaches up into the blank band, and may watch the cursor.

Other places:

8. charts, slides and a paper's diagram

Charts and diagrams wear the same pencil, and the data plays the characters' part as the only saturated thing (principle 4). The matplotlib styles in figures/ carry these rules; stack an overlay for the medium:

plt.style.use(['phy.mplstyle', 'phy-slides.mplstyle'])  # Slides and the web.
plt.style.use(['phy.mplstyle', 'phy-print.mplstyle'])   # Print: white, in the host document's typeface.

Data colours come from phy's characters: each is a character's colour taken down in the house shade step (principle 5) until it holds 3:1 on the paper, and where two still sat too close, one took another step. python3 figures/palette.py derives them again and prints the tests.

seriescolourshade
1, blue#1092ca#1777a4
2, red#a74126#931800
3, slate#4d6295#284a85
4, ochre#a98f12#8c7609
5, pink#d56b75#c34758

Charts:

Slides. A slide is the notebook page ×1.5: at 1280×720 the rules are 48px apart, 15 to a slide, and the title is 96px. A chart on a slide takes the slides overlay's sizes instead.

A paper's main diagram, the teaser or method overview that people remember the paper by and redraw in their own talks, is where the pencil shows; the paper's text keeps the venue's type. figures/phy-diagram.sty carries it for TikZ, and figures/diagram.tex is an example.

9. the avatar and characters

10. code

Code is a language of its own: American English, formal, and clean in the sense of Robert C. Martin's Clean Code. The dial, the lower-case chrome and the humour stop at its edge.

11. working with phy

12. pictures, films and sound

A piece made to delight (a picture, a film, a game) is looked at before it is read. Everything above holds in it, except where this section, or a departure it declares (§2), says otherwise.

The picture. A frame is composed, not filled:

The characters stay as drawn; the world is free. As in cel animation, the characters are drawn as they always are (§9), and the place around them takes its own colours (principle 4) and its own light:

References, each for the one thing to take from it (principle 2), never its whole look:

Sound and pace:


phy's fwiends: the gallery and its characters

This file adds what only this project needs to phy's style guide (STYLE.md): the gallery page, the rules for drawing a friend with src/phyfriends.js, and the rules for putting friends together in films and games. Where the two disagree, STYLE.md wins unless this file says why. The site's style.html shows both, one after the other, and section N of STYLE.md stays at style.html#sN. Run python3 tools/pf.py style after editing either.


index.html (the gallery), style.html (the guides) and specimen.html (the kit at work) all wear the kit, and turn to the dark sheet where the reader's system asks for it (STYLE.md §4). The paper is design C of the mockups in design/; A and B are kept in design/backup/. The specimen sits at 50, and its dial turns the face, never the case, to 0 or 100 until a reload. The gallery is a hobby project, so it sits at 100 (STYLE.md §5).

The page has five parts, in this order:

  1. The title, phy's fwiends, in the hand: the project's name wherever people read it (phy_friends is only the repository's and the code's).
  2. One line of intro: "a few fwiends in coloured pencil, each one drawn from a hundred-odd lines of code."
  3. The nav, as on the other two pages: fwiends, style guide, specimen.
  4. The stage, a group photo: the fwiends stand together on pencilled ground and all watch the cursor.
  5. The footer: "characters belong to the people named under them"; a credit to Terry's owner, who tuned the fwiends' proportions by eye on the tuning page; the style guide; and the source.

The stage:

Labels, three rules tall: the name in the hand, 31px (26px on phones, 22px on the smallest), a step above an h3, since it is read from further away; the species in small print, two or three lower-case words ("tuxedo eevee"); and the owner's handle in small print, linking to their page (phy's reads "that's me · linkedin"). On the smallest phones a long species or handle wraps onto a second rule rather than run into a neighbour.

Each friend is a button with a short name (aria-label), "say hi to Terry", so that a screen reader or a voice gets to the point; what it looks like is a hidden description (aria-describedby): "a yellow plush toy with huge floppy ears, a fluffy bib and dark felt soles". The specimen's portrait is named and described the same way.

The cast (characters/cast.js) is the source of each name, species, credit and consent. The page repeats them by hand (the labels, the aria-labels, the host, the footer's credit and the link preview's alt text); python3 tools/pf.py test names the friend and field wherever the two disagree, and fails any friend whose owner has not agreed to the gallery. Every page that loads all the friends loads them in the cast's order, and python3 tools/pf.py pages writes their script tags.

dodon't
click one to say hiClick a character to interact!
a few fwiends in coloured pencil, each one drawn from a hundred-odd lines of code.Welcome to a magical world of adorable characters
characters belong to the people named under themAll rights reserved.

Saying hi, and how they take it. src/live.js, which any page can use, brings the fwiends to life, and their feelings come from the emotion library:

The opening. The first time the page opens in a tab, the title writes itself and the fwiends come in by roll call. A reload, a step back through the history, a link to a fwiend, reduced motion, "keep still" and the link preview show everything in place.

The favicon and link preview come from python3 tools/site_assets.py, run after any character changes: the favicon is phy's head, and the preview (1200×630) is the page photographed in the narrowest window where every friend stands in one row, then scaled down.

drawing a friend

The factory (src/phyfriends.js) encodes these rules. A friend is a spec of about 100 lines, a palette plus shape parameters, not a traced path. Before turning someone's picture into one, ask the owner how the character is written (the name's capitalisation, the species and the pronoun, "they" until told), take one picture as the colour standard and say which, and keep private references in a git-ignored private/ folder.

Coloured pencil (STYLE.md §9):

Proportions, in head space: a big head on a small body.

Shape vocabulary, layered ears, head, face mask, blush, eyes, mouth, then hair on top (bangs may cross the face):

Turning round comes from the spec, not from new drawings. Every fwiend but Claude turns as South Park's cut-outs do, a quarter turn at a time (pose.facing): side-on to either side, or its back to the viewer, by rules that every fwiend shares (PhyFriends.TURN), so a change to the body plan turns with it. Where the true turn looks worse than a cheat, the cheat wins.

Feelings come from the pose, not from new drawings. The emotion library (src/emotion.js) has twenty-three, each made of parts every fwiend on the house template has: the eye shapes (happy, closed, squint and the dizzy swirl), a lid over the open eyes (lid, slanted by lidTilt for a sad or cross look), the blush spreading (flush), a mouth (w, smile, frown, o, flat, wobble, open), the ears, the tail and the posture, and, standing, the arms and a gesture (paws on the hips when proud, a shrug when confused). Each shape is drawn from the eye's or the mouth's own size and colour, so a spec needs nothing for its feelings, and a new fwiend has them all. Body language follows from how pleasant and how alert a feeling is, so feelings that sit close look alike. eyes.arc and eyes.stroke set how wide and thick the happy and closed strokes are.

Keep the spec small. Tune the fluffy, star and ear parameters rather than writing raw nodes, and match the set in the spec's structure as well as in its look.

Claude, the exception. The last fwiend is Claude, the model that drew the others, as Claude Code draws it on its welcome screen: three lines of block characters. It keeps every rule above but the shape, which comes from the glyphs. A terminal cell is about twice as tall as it is wide, so a quadrant pixel is one unit wide and two tall, drawn 16 head units to the unit, which makes the eyes as tall as the house's: a terracotta block whose body is its head, pill eyes where the glyphs cut their notches, a stub of an arm on each side and four short legs.

Motion, on the page and in a scene (STYLE.md §9):

scenes: films and games

Beyond the gallery, the fwiends appear in scenes built with film/scene.js: short films, listed in demo/index.html, and in time games. A scene is a sheet of ruled paper measured in head units, a rule every 54, so a seated fwiend is five rules tall, as in the gallery. Everything happens at a time on the scene's clock, so a film plays in a page and is filmed frame by frame (pf.py film) from the same script.

Who knows whom. Every fwiend belongs to someone, and not every owner knows every other one. phy, the host, knows everyone; any other two are strangers until phy says their owners know each other (characters/cast.js). The scene checks these rules and refuses to break them.

strangersknow each other
share a scene, take turns, look at each other, react to the same thingyesyes
greet from a distanceyesyes
stand close, talk, hand something over, play on one sidenoyes
touch, compete head to head, teasenoonly if both owners opt in

Props and sets. Everything in a scene that is not a fwiend is drawn as a fwiend is, so that the sheet keeps one hand.