A utopian language. Simple, logical, and culturally neutral.
Papsa is a constructed language in the logical-language tradition — comparable to Lojban, but with a focus on simplicity and elegance. Papsa is designed with "Utopia" in mind, and is not based on any existing language or culture. It has no inflection, a lightweight grammar, and a 14-letter alphabet. The shape of words and the letters they contain provide a guide to their meaning (e.g. words that start with "k" are associated with the element of fire). For more on the conceptual underpinnings of the language, see the Philosophy appendix.
This page is primarily a description of Papsa in plain English. But it also contains a formal specification (written in JavaScript) that validates all the example text and powers the interactive tool below. Code appears alongside the relevant text, but is collapsed by default. Click the code-block header to expand it.
Click or hover on Papsa words to see their meanings and a guide for pronunciation.
Type any Papsa sentence to see its structure and a rough English gloss. Enter a single Papsa word for its full definition, or a single English word for the closest matches in the Papsa dictionary.
The Papsa alphabet, at heart, has 14 letters. Each letter covers a range of sounds, and speakers are free to use whatever pronunciation in that range is clearest and easiest for them. Each of these letters is associated with a conceptual atmosphere that hints at the meanings of words that contain them.
| Letter | Acceptable sounds (IPA) | Atmosphere | |:------:|:-----------------------------:|-------------------------| | `N` | n,ŋ,ɲ,ɳ,ɴ | Spirit / Mind | | `M` | m,ɱ | Water / Life | | `K` | k,g,q,ɢ,ɠ,ʛ | Fire / Energy | | `T` | t,d,ʈ,ɖ,c,ɟ,ɗ,ᶑ,ʄ | Metal / Structure | | `P` | p,b,ɓ | Wood / Community | | `F` | f,v,θ,ð,ɸ,β | Earth / Substance | | `H` | h,ɦ,x,ɣ,χ,ʁ,ħ,ʕ,ʜ,ʢ | Air / Freedom | | `C` | ʃ,ʒ,ç,ʝ,ʂ,ʐ,ɕ,ʑ,ɬ,ɮ | Shadow / Negation | | `S` | s,z | Time / Abstraction | | `L` | l,ɭ,ʎ,ʟ,ɹ,ɻ,ɰ,r,ʀ,ɾ,ɽ | Distinction | | `U` | u,o,ʊ,ɯ,ɤ,ø,y,ʉ,ʏ,ɵ | Entities / Actors | | `A` | ə,ɐ,ä,ɘ,ɞ,a,ɑ,æ,ɒ,ɶ,ɛ,ɔ,ʌ,œ,ɜ | Relationships / Actions | | `I` | i,e,ɪ,ɨ | Objects / Qualities | | `!` | ǀ,ǃ,ǁ,ǂ,ʘ,ʔ,ʡ | Grammatical Boundary |
The first nine letters of the Papsa alphabet are called the "elementals." The first six of those (associated with the numbers 0 through 5) are the "primals," which are in turn split into three "vitals" (Spirit, Water, and Fire), and "materials" (Metal, Wood, and Earth). The last three elementals are "ethereals" (Air, Shadow, and Time). The primals, in particular, are associated with Papsa's base-six number system. For more details, see the number section of the appendix.
Papsa letters can also be annotated to specify an exact pronunciation.
(In the Papsa script we do this using diacritics: `g`.) In this way,
it can serve as a phonetic alphabet, if needed, such as when writing a name.
To make things easy on English readers, the standard way to romanize Papsa uses a small set of lowercase letters where each letter represents a single Papsa letter. For example, we use the letter "c" for the "sh" sound to clarify it's not the "s" letter plus the "h" letter. And because Papsa letters can be pronounced in a variety of ways, letters are chosen to roughly match Anglophone expectations about the most normal pronunciation. For example, we will see both "k" and "g" showing up in various words, despite those technically being the same Papsa letter. See the orthography appendix for more details on the Papsa script.
| Romanization | Papsa Letter | Sound (IPA) | Sounds like the end of... | |--------------|--------------|-------------|---------------------------| | n | `N` | n | on | | ŋ | `N` | ŋ | tong | | m | `M` | m | am | | k | `K` | k | ick | | g | `K` | g | wag | | t | `T` | t | it | | d | `T` | d | add | | p | `P` | p | app | | b | `P` | b | ab | | f | `F` | f | if | | v | `F` | v | of | | θ | `F` | θ | with | | h | `H` | h | hah | | x | `H` | x | loch | | c | `C` | ʃ | ash | | j | `C` | ʒ | beige | | s | `S` | s | sass | | z | `S` | z | is | | l | `L` | l | all | | r | `L` | ɹ | or |
const baseLetter = (ch) => {
if (clickChars.has(ch)) return '!';
const baseRow = alphabet['Letter'][ch];
if (baseRow) return baseRow['Letter'];
const lower = ch.toLowerCase();
const romRow = engConsonantRomanization['Romanization'][lower];
if (romRow) return romRow['Papsa Letter'];
const ipaRow = alphabet['Acceptable sounds (IPA)'][lower];
if (ipaRow) return ipaRow['Letter'];
throw new Error(`Unknown Papsa character: ${ch}`);
};
const baseLetters = (wrd) => [...wrd].map(baseLetter).join('');
const letterIdx = (ch) => alphabet['Letter'][baseLetter(ch)].index;
const primalNum = (ch) => {
const idx = letterIdx(ch);
if (idx < 6) return idx;
throw new Error(`Not a primal: ${ch}`);
};
const clickChars = new Set(['.', ',', "'"]);
const vowelChars = new Set(['U', 'A', 'I']);
const vowelIdxs = new Set([...alphabet['Letter']].filter(([ch]) => vowelChars.has(ch) ).map(([,r]) => r.index));
const consonantIdxs = new Set([...alphabet['Letter']].filter(([ch]) => !vowelChars.has(ch) && ch !== '!').map(([,r]) => r.index));
const isV = (i) => vowelIdxs.has(i);
const isC = (i) => consonantIdxs.has(i);
const wordIdxs = (wrd) => [...wrd].map(letterIdx);
The sound at the start of the English word "jump" is written with the two letters "dj" in romanized Papsa. Likewise, the sound at the start of "choose" is written as "tc."
The `U` vowel is typically written with "u" and sounds like the vowels in "moo,"
"shoe," "two," et cetera. The vowel `I` is typically written with "i" and sounds
like the vowels in "me," "she," "tee," et cetera. The vowel `A` is written with "a"
and can have a wide range of sounds, such as the vowels in "nut," "mop," "cat," and "caught." It's
unusual for the specific "a" sound to be indicated for English speakers, since English words don't
really have consistent vowels in the first place. Check the dictionary for specific
pronunciations.
Vowels can be combined into diphthongs and longer glides. Instead of writing "ii" we typically write "ei", and instead of writing "uu" we usually write "ou," to make things more natural to English readers. The "a" vowel never combines with itself. Glides are never romanized using the letters "w" or "y" — Papsa always interprets those sounds as vowels.
| Diphthong | Sounds like the vowels in... | |-----------|------------------------------| | ei | say | | ia | yawn | | iu | you | | ai | eye | | au | pow | | ui | we | | ua | want | | ou | owe |
Papsa is distinct from most natural languages in that it has a click. The letter
`!` is usually written as a "." and basically only ever occurs as a stand-alone word
that marks the boundary between sentences. Because it is similar to a period, it is often written
without any space between it and the preceding word. It is usually pronounced with a dental click
— the tooth-sucking noise that is often written as "tsk!" in English. (When written to close a
quote it's written as `,` and pronounced as a more emphatic click. In the very rare cases where it
shows up mid-word it's pronounced as a glottal stop and written with an apostrophe.)
Papsa has four types of words, determined entirely by shape — no declaration needed. Papsa has no inflection: words never change form for tense, number, case, gender, or any other grammatical category.
| Type | Shape | Examples | |--------------|-----------------------------------------------------------|--------------------------------------| | Grammar word | ≤1 consonant group & 1 vowel group (or just `.`) | `na`, `ei`, `ul`, `uau`, `intc`, `.` | | Core word | starts and ends with a consonant (CVC minimum) | `nup`, `bik`, `maukuh` | | Name | ≥2 vowel groups, starts or ends with vowel, not a pronoun | `tina`, `djani`, `papsa` | | Pronoun | only 1 consonant group with vowel groups on both sides | `ina`, `ardja`, `apsa` |
const wordType = (ixs) => {
const cvPattern = ixs.map(i => isV(i) ? 'V' : 'C').join('').replace(/(.)\1+/g, '$1');
if (cvPattern === 'C' && ixs.length === 1) return isC(ixs[0]) ? 'pronoun' : 'grammar';
if (cvPattern === 'C') throw new Error(`Unclassifiable: ${ixs}`);
if (cvPattern === 'V' || cvPattern === 'VC' || cvPattern === 'CV') return 'grammar';
if (cvPattern === 'VCV') return 'pronoun';
return /^C.+C$/.test(cvPattern) ? 'core' : 'name';
};
const vowelGroups = (ixs) => ixs.map(i => isV(i) ? 'V':'C').join('').match(/V+/g)?.length ?? 0;
const grammarWords = {};
const coreWords = {};
const names = {}; // Also technically contains the pronoun version of the letter names.
const tokenParsers = [ // Defined as a list so we can later extend with number tokens.
// base = baseLetters(s), ixs = wordIdxs(s). We pass all for efficiency.
(s,base,ixs) => grammarWords[base] && { ...grammarWords[base], rawTok: s, type: 'grammar' },
(s,base,ixs) => coreWords[base] && { ...coreWords[base], rawTok: s, type: vowelGroups(ixs) > 1 ? 'compound' : 'root' },
(s,base,ixs) => {
const structure = wordType(ixs);
if (structure === 'name' || structure === 'pronoun')
return { ...(names[base] ?? {}), rawTok: s, type: structure, probablyForeign: ! names[base] };
},
];
Pronouns are based on names: take the first two vowel groups and the consonants that come
between if the name starts with a vowel (`ardjantina` → `ardja`),
or the last two if it starts with a consonant (`babi` → `abi`).
As with pronouns in any language, ambiguity can sometimes occur if two names are very similar,
and Papsa speakers will occasionally use unconventional pronouns for clarity.
Each letter has a full name that follows the pattern "a_apaun" for consonants, and "_lupaun"
for vowels. Thus the full name of "n" is "`anapaun`", and the full name of "a" is
"`alupaun`." But almost everyone refers to letters by the pronoun that drops the
"`paun`" suffix. If a consonant (not vowel or click) is written by itself, this is
interpreted as the "a_a" pronoun. (This is allowed in writing only. It's still pronounced with
vowels.) The click is "`a'apaun`," where the "`'`" is a rare instance
of `a'a` being used in a word (besides `.`) and is pronounced as a
glottal stop.
for (const [ltr,row] of alphabet["Letter"]) {
const r = ltr === '!' ? "'" : ltr.toLowerCase(); // romanized letter
const meaning = `letter “${ltr === '!' ? ltr : r}”`, notes = row['Atmosphere'].replace(/<[^>]*>/g, '').trim();
const [short, full] = isV(row.index) ? [r+'lu', r+'lupaun'] : ['a'+r+'a', 'a'+r+'apaun'];
names[baseLetters(short)] = { word: short, meaning, notes };
names[baseLetters(full)] = { word: full, meaning: meaning+' (full name)', notes };
if (isC(row.index)) names[ltr] = names[baseLetters(short)]; // written alone, read as a_a
}
Core words are either single-syllable "root words," or longer "compounds" formed by joining root words. Compounds are usually related to the root words that make them up, with the leftmost/earliest base anchoring the grammar and core meaning, and the later root words being like modifiers. When someone comes up with a new word via compounding, it's important to adhere to a few rules and conventions for which letters are retained and so on, to match Papsa's general phonotactics. See the appendix for the full rules.
While not technically a form of inflection, Papsa speakers have a convention of giving names
to inanimate objects and entities in the course of talking about them, usually by adding a
leading vowel to the start of a core word. This is a bit like tagging the noun as "definite" by
using "the" in English. For instance, when a conversation starts, a person might refer to a fire
as `guk`, but then later use the name `aguk` to refer to that
particular fire. (And if the thing has a very long word, they might just use the pronoun version
of its name.)
Papsa names are always one word. (If they weren't, they would cause grammatical confusion.)
When someone has a first and last name, Papsa represents that by mashing them together. The same
goes for multi-word scientific names. Canis familiaris (dog) becomes
`kanafamiliara`. (See the taxonomy of Earth organisms for
more.)
Papsa has a fixed set of short words that are used for grammatical purposes. Many of these follow regular patterns based on their vowels and/or primals. For example:
| Word(s) | Usage |
|:----------------------------------------------:|------------------------------------------------|
| `ei` | Augment as an afterthought |
| `un`, `um`, `uk`, `ut`, `up`, `uf` | Subject with {0, 1, 2, 3, 4, 5} augments |
| `an`, `am`, `ak`, `at`, `ap`, `af` | Verb with {0, 1, 2, 3, 4, 5} augments |
| `in`, `im`, `ik`, `it`, `ip`, `if` | Object with {0, 1, 2, 3, 4, 5} augments |
| `ui` | Possession/association as an afterthought |
| `nau`, `mau`, `kau`, `tau`, `pau`, `fau` | Subject with {0, 1, 2, 3, 4, 5} associations |
| `na`, `ma`, `ka`, `ta`, `pa`, `fa` | Verb with {0, 1, 2, 3, 4, 5} associations |
| `nai`, `mai`, `kai`, `tai`, `pai`, `fai` | Object with {0, 1, 2, 3, 4, 5} associations |
| `ou` | Grouping/addition as an afterthought |
| `snau`, `smau`, `skau`, `stau`, `spau`, `sfau` | Subject with {0, 1, 2, 3, 4, 5} additions |
| `sna`, `sma`, `ska`, `sta`, `spa`, `sfa` | Verb with {0, 1, 2, 3, 4, 5} additions |
| `snai`, `smai`, `skai`, `stai`, `spai`, `sfai` | Object with {0, 1, 2, 3, 4, 5} additions |
Some grammar words are extremely common, like `.`, while others, like
`antc`, almost never show up in practice, and are more for completeness.
See Syntax for usage.
| Word(s) | Usage | Gloss |
|:----------------------------------------------:|------------------------------------------------|--------------------|
| `.` | Sentence divider | . |
| `uas`, `iur` | {"And", "But"} | {and, but} |
| `tu` | Addressing a specific listener | VOC |
| `ouei`, `ceiou` | {Command, Prohibition (anti-command)} mark | {IMP, PROH} |
| `sou` | Yes/no question mark | Q |
| `eis` | Complex question mark | WH.Q |
| `ous` | "what/where/why/..." | WH |
| `lu`, `la`, `li` | Start {subject, verb, object} clause | {S.CL, V.CL, O.CL} |
| `ul`, `al`, `il` | End {subject, verb, object} clause | {S.CL, V.CL, O.CL} |
| `θu`, `tci` | Preposition mark (clause is {subject, object}) | {PREP, PREP.REV} |
| `untc`, `antc`, `intc` | End preposition ({subject, verb, object} next) | {→S, →V, →O} |
| `uip`, `uin` | {"Mine", "Yours"} | {my, your} |
| `uau` | Foreign word mark | FOREIGN |
| `uai` | Foreign speech mark | FOREIGN |
| `iau` | Open quote mark (closed with `,`) | QUOTE |
// A "{a, b, ...}" in a cell gives one option per word in the row, in order.
const usageOf = (word, row, col='Usage') => {
const words = row['Word(s)'].split(',').map(w => w.trim());
return row[col].replace(/\{([^}]*)\}/g, (_, opts) => {
opts = opts.split(',').map(o => o.trim());
if (opts.length !== words.length) throw new Error(`{${opts}} needs one option per word in ${words}`);
return opts[words.indexOf(word)];
});
};
for (const [word,row] of phraseWordMeanings["Word(s)"]) {
const meaning = usageOf(word, row);
const phraseMode = ['aug','assoc','add'].find(m => meaning.toLowerCase().includes(m));
const numLetter = [...word].find(c => "nmktpf".includes(c));
const slot = numLetter ? meaning[0] : undefined;
const phraseSize = numLetter ? primalNum(numLetter)+1 : null;
const gloss = { aug: 'AUG', assoc: 'ASSOC', add: 'AND' }[phraseMode] + (phraseSize ? phraseSize-1 : '');
grammarWords[baseLetters(word)] = {word, meaning, phraseMode, slot, phraseSize, gloss: slot ? slot+'.'+gloss : gloss };
}
const VOWEL_SLOT = { u: 'S', a: 'V', i: 'O' };
for (const [word,row] of miscGrammarWordMeanings["Word(s)"]) {
const r = {word, meaning: usageOf(word, row), gloss: usageOf(word, row, 'Gloss')};
if (/^l[uai]$/.test(word)) { r.clause = 'open'; r.slot = VOWEL_SLOT[word[1]]; }
else if (/^[uai]l$/.test(word)) { r.clause = 'close'; r.slot = VOWEL_SLOT[word[0]]; }
grammarWords[baseLetters(word)] = r;
}
The meanings of Papsa's core words change depending on how they are used. For instance, the
word `bik` can mean "food" when used as a noun, but means "to eat" when used as a
verb, and "edible" when used as an adjective. This is similar to how "feed" in English is a verb,
but can also be a noun. Whether this means Papsa has many words with the same spelling, or the
same underlying word changes to fit the slot is up to interpretation, but the standard story is
that all Papsa words with the same spelling are the same word. Papsa has no
homonyms (or homophones) with unrelated meanings.
As a rule of thumb, the meanings of a word tend to follow a pattern based on its first vowel
group. If the first vowel group is "u", "ui", "ua", "au", or "ou", the noun form of the word is
usually the agent/subject that does the action of the corresponding verb. If the first vowel group
is simply "a", the noun form is usually the action itself. And if the vowel group is "i", "ei",
"ia", "ai", or "iu", the noun is usually either the essential quality of the relationship, or
the thing that is being acted upon. For example, because the vowel in `bik` is "i",
and the verb is "to eat", the noun is "that which gets eaten" — food! Adjectives usually reflect
the essential character of the noun, while adverbs are often harder to predict. Consult the
dictionary when in doubt — all this is just a heuristic.
Another heuristic is that the first letter in a core word indicates the primary elemental atmosphere, and the following consonants indicate secondary associations, with the letter "l" being used to indicate something weird or unexpected. (No core words start with "l".)
It's typical for words to not match up perfectly with English. Multiple translations are provided to help triangulate meaning. Short words tend to be more general/vague, while longer words provide specificity.
`N` — Spirit (Mind, Truth, Personhood, Perception, Emotion, Judgment, Definition)| Word | Noun | Verb (Transitive) | Notes | Adjective | Verb (Intransitive) | Adverb | |------|------|-------------------|-------|-----------|---------------------|--------| | `nuŋ` | mind, thinker, intellect, intelligence, cognition | to think about, to consider, to imagine | | intellectual, conscious, aware, perceptive, reasonable, thoughtful, insightful | to think, to reason, to dream, to speculate, to hypothesize | mentally, intellectually, psychologically | | `nup` | person, people, agent, human, society, man, woman, child, boy, girl, character, personality, moral patient, moral agent | to identify with, to express oneself through, to be characterized by | | personal, personalized, societal | to be a person, to have a personality | personally, socially | | `nuh` | audience, reader, receiver, listener, auditor, you, y'all | to hear, to listen to, to read, to recognize, to perceive | | attentive, receptive, perceptive | to attend, to pay attention | attentively, receptively | | `naun` | treasure, wealth, resource, virtue, benefit, boon | to be good for, to have value to, to attract, to benefit | | good, moral, right, valuable, healthy, excellent, lovely, wonderful, great, glorious | to be good, to be valuable, to be beneficial | well, excellently | | `nuif` | edge, border, maximum, minimum, boundary, bound, limit, fringe, margin | to border, to bound, to limit, to be the limit of, to edge, to define, to demarcate | | extreme, maximal, minimal, edgy, borderline, marginal, limiting, limited, bounded, finite, defining | to be an edge, to be a limit | extremely, marginally, finitely | | `nat` | play, gaming, game, adventure, make-believe | to play with, to toy with, to explore, to go on an adventure to, to pretend that | | playful, adventurous, pretend, silly | to play, to pretend, to explore | playfully | | `niŋ` | thought, idea, concept, knowledge, understanding, awareness, insight, model, fact | to know, to be aware of, to comprehend | | knowledgeable, aware, insightful, cognizant, conscious, informed, learned, educated, sage | to understand, to be wise, to be sage | knowledgeably, insightfully | | `neim` | feeling, sensation, emotion, experience, perception, texture | to feel, to sense, to experience, to touch, to perceive | There is a deliberate map-territory vagueness around `neim` and its derivatives (e.g. `neimnaun`). While the vowel pattern indicates that the noun form is that-which-is-felt, we often translate it as the feeling itself or even the one who is experiencing the feeling. Philosophically, this stems from a stance that the true object of `neim` is in the mind. Getting slapped with a cactus is not *objectively* painful (congenital analgesia is a thing, for example), and so we can attach the notion of painfulness to a wound, the experience of a wound, or one who is wounded. | sensitive, emotional, sensory, intuitive, tactile | to feel, to be sensitive | sensitively, emotionally, intuitively | | `nik` | goal, target, ends, mission, objective, destination | to aim at, to work towards, to steer towards, to pursue, to seek, to optimize for | | targeted, optimum, desired, purposeful | to strive, to work towards a goal | purposefully, deliberately | | `nis` | truth, yes | to prove, to confirm | Also used as an affirmative response to yes/no questions | true, accurate | to prove it, to confirm it | truly, accurately | | `nais` | measurement, measure, count, quantity, assessment, evaluation, judgment, analysis | to measure, to weigh, to count, to assess, to evaluate, to judge, to analyze | The subject is the measurer or the measurement (both work, for the same reason `neim` is read flexibly), and the object is the thing being measured. When a number word is the verb, the subject is the thing being measured and the object is the dimension or unit. | measured, measurable, assessable, quantifiable, quantified, precise, rational | to judge the situation, to be discerning | analytically, judgmentally, rationally, precisely, numerically |
`M` — Water (Flow, Life, Instinct, Desire, Cycles, Curves, Origins)| Word | Noun | Verb (Transitive) | Notes | Adjective | Verb (Intransitive) | Adverb | |------|------|-------------------|-------|-----------|---------------------|--------| | `mum` | water, liquid, fluid, moisture, drink, lubricant | to hydrate, to moisten, to wet, to lubricate | | wet, watery, aquatic, damp, moist, soggy | to flow, to be wet, to be liquid | fluidly, smoothly, continuously | | `muk` | life, organism | to experience, to live through | | healthy, living, alive, organic, natural | to live, to grow, to thrive | vitally, organically | | `mut` | body, frame, form, physique, embodiment, representation, build, meat, substance, constitution | to embody, to represent, to obey, to support, to sustain | Focuses on the physical form that reflects the will. | bodily, physical, representative, substantial, constituative, obediant | to take form, to be a body | physically, substantively | | `muf` | receiver, recipient, acquirer, owner | to get, to receive, to obtain, to purchase, to acquire, to collect, to gain, to retrieve, to earn, to take, to accept | | grasping, purchasing, acquisitive | to gain, to earn | acquisitively | | `muc` | soap, cleanser, cleaning, cleaner, purification, filter, disinfectant | to clean, to purify, to cleanse, to clear, to erase, to wash | | clean, pure, purified, sterile | to clean, to wash | cleanly, purely | | `mauk` | creature, animal, beast, fauna | to react to, to respond reflexively to | | animate, instinctive, wild, zoological, animalistic, instinctual | to act on instinct, to behave instinctively | instinctively, wildly | | `mam` | change, transformation, alteration, adaptation, shift, evolution, function, mapping | to become, to change into, to turn into, to evolve into, to map to | | changing, transformative, adaptable, adaptive, adapted, shifting, evolving, alterable, mutable, flexible | to change, to shift, to evolve, to adapt | dynamically, adaptively | | `maz` | curve, bend, circle, sphere, ball, loop, orb, lump, bulge, rhythm, cycle | to encircle, to surround | | round, circular, curved, bent, looping, repetitive, cyclic | to curve, to cycle, to return, to repeat, to swerve, to bend, to curl, to reverse | again, cyclically, repeatedly | | `min` | desire, wish, want, need, craving, yearning, hope | to want, to desire, to wish for, to crave, to seek, to hunt, to hope for, to miss, to lack, to need, to require | | seeking, hoping, wanting, desirous, wishful, hopeful, needy, incomplete, unfulfilled, eager, ambitious, lost | to yearn, to hope | longingly, eagerly, ambitiously | | `main` | seed, newborn, start, child, baby, beginning, novelty, youngster, newcomer, rookie | to be the birth of, to be the inception of, to start, to christen, to be the moment of creating, to be the renewal of, to be the process of growing | About the new thing and the event that brings it about. Compare `fun`, where a thing comes from, and `tum`, deliberate making. | new, young, recent, rejuvenated, revitalized, refreshed, renewed, inexperienced, green, unseasoned, budding, nascent, early | to be new, to begin | initially, originally |
`K` — Fire (Heat, Light, Electricity, Energy, Motion, Power, Enhancement)| Word | Noun | Verb (Transitive) | Notes | Adjective | Verb (Intransitive) | Adverb | |------|------|-------------------|-------|-----------|---------------------|--------| | `guk` | fire, flame, blaze, inferno, torch, candle, plasma, star, sun, reactor, forge, heat | to burn, to heat, to ignite | | scorching, fiery, warm, hot, intense, strong | to burn, to smolder, to blaze | intensely, heatedly | | `kuh` | traveler | to go to, to travel to, to journey to, to progress towards | | moving, travelling, speedy, fast | to travel, to move, to proceed, to advance, to migrate | swiftly, quickly | | `kud` | force, cause, generator, source, reason | to cause, to compel, to force, to push, to oblige | This is the word for when someone's act of will determines what happens. When things must be a way due to some static arrangement or set of laws, including the laws of nature, use `zuip`. Governments can be seen either way. | compelling, forceful, pushy, obligatory, mandatory | to push | forcefully, compellingly | | `gab` | glue, web, net, binding force, trap | to grab, to grapple with, to entangle with, to stick to, to bind, to ensnare, to trap, to capture | | trapping, ensnaring, binding, tangled, caught, bound, sticky | to cling, to grapple, to tangle, to snag | tenaciously | | `gaug` | electricity, zap, shock, current, charge, discharge, lightning | to electrify, to charge, to spark, to activate, to power on, to shock, to energize | | electrifying, shocking, zapping, energetic, sparking, activating, active, powered, charged, electric | to spark, to have voltage | electrically, energetically | | `guih` | light, glow, star | to illuminate, to light up, to shine on, to reveal, to distract | | shiny, glowing, bright | to shine, to glow, to catch attention | brightly, luminously | | `kan` | enhancement, improvement, amplification, healer, teacher, assistant, technology, booster, exponentiation | to enhance, to improve, to raise, to empower, to uplift, to amplify, to strengthen, to accelerate, to intensify, to increase, to exponentiate | In mathematics, the exponent is the subject and the base is the object: `suk kan sat` is 3². | powerful, empowering, supportive, enhancing, uplifting, elevating, accelerating, strengthening, reinforcing, promoting, healing | to be an enhancement | powerfully, increasingly | | `kat` | tension, stress, strain, readiness, spring, battery | to await, to brace for, to be ready for, to wait for | | tense, tight, strained, coiled, primed, prepared, waiting | to be tense, to tense up, to be ready, to wait, to strain | tensely, readily | | `kim` | instrument, limb, body, puppet, theme, patient, object, thing that is moved, thing that is acted upon | to move, to adjust, to act on, to manipulate | | active, transient, dynamic, energetic | to act, to move, to sway, to wiggle | actively, dynamically, energetically |
`T` — Metal (Stone, Armor, Mass, Structure, Stability, Endurance, Tools, Construction, Simplicity)| Word | Noun | Verb (Transitive) | Notes | Adjective | Verb (Intransitive) | Adverb | |------|------|-------------------|-------|-----------|---------------------|--------| | `dut` | stone, metal, rock, boulder, monolith, barrier, difficulty, wall, fence, obstacle | to stop, to block, to resist, to impede, to halt, to prevent, to inhibit | | hard, difficult, obstinate, tough | to hold fast, to stand firm | firmly | | `tum` | maker, builder, craftsman, creator, producer | to make, to create, to build, to craft, to produce | Deliberate making, craft. Compare `fun`, provenance. | creative, constructive, productive | to build, to craft | creatively, constructively, productively | | `dup` | guard, protector, defender, armor, shield, shell | to guard, to protect, to defend, to watch over, to nurture, to parent | | guarding, protective, vigilant, secure, defensive, guarded, tough | to keep guard, to protect | protectively, vigilantly, securely | | `tuah` | point, tip, spike, pin, skewer | to pierce, to point at, to pin, to penetrate, to fix in place, to stab | Refers to the tip of a spear rather than the spear itself. Sometimes refers to singular points, including bullets. More metaphorically, can be used for theses, punchlines, or throughlines. | sharp, piercing, pointed, penetrating | to point, to stab | pointedly, sharply | | `taut` | base, root, foundation, anchor, foot, chair, table, desk, door, gate, window, fixture | to anchor, to stabilize, to root, to support, to establish, to ground, to plant | Many compound words that start with `taut` have a subject that is a piece o furniture and a subject that is the user. | rooted, foundational, anchored, original, fundamental, stable, unmoving, supportive, solid | to be steady, to be stable | fundamentally, solidly, stably | | `taz` | plane, surface, layer, expanse, area, level, plain, field, cloth, sheet, covering, coating | to flatten, to level, to plane, to spread out, to lay out, to lay down, to spread evenly, to balance, to divide evenly | This is the most common way to refer to people who are lying down, such as sleeping or resting, and can even include people who are sitting in a relaxed way. When used as a verb, subject is implied to be the one making things flat and the object is the stuff that's flattened, but there's a loose implication that the subject also spreads out in the process. | flat, even, uniform, smooth, horizontal, wide, broad, expansive, planar, prone, flattened, leveled, plain, featureless, direct, simple, uncomplicated, balanced, fair, equal | to spread out, to lie flat, to balance | flatly, evenly, uniformly, simply, directly | | `tait` | giant, colossus, mass, weight | to enlarge, to blow up, to expand, to inflate, to fill out, to thicken, to grow | To express "X is big", we place X as the object in a sentence with this as the verb, usually leaving off the subject. | large, massive, big, heavy, tall, long, wide, huge, giant | to make it bigger | massively, heavily | | `tiŋ` | core, center, heart, essence, nucleus, kernel | to find the center of, to focus on, to get to the heart of, to center, to condense, to consolidate | | core, central, essential, focused, condensed | to focus, to concentrate, to center | centrally, essentially | | `tik` | tool, device, machine, clothing, construct, artifact | to use, to wear, to wield, to deploy | Compounds built from `tik` often have a verb form where the user of the tool is the subject (or occasionally the tool itself, in violation of the usual vowel pattern), and the obeject is the thing the tool is being used on, rather than the tool itself. | useful, artificial, mechanical | to use something | usefully, mechanically | | `tif` | building, structure, shelter, home, nest, lair, lodge, hive, house, housing, refuge, sanctuary, mansion, skyscraper, office, apartment, church, residence, dwelling | to inhabit, to live in, to reside in, to be inside, to occupy | | built, structural, sheltering, enclosing, constructed, architectural | to dwell, to reside | structurally, architecturally |
`P` — Wood (Plants, Food, Connection, Community, Family, Friendship, Communication, Trade, Civilization)| Word | Noun | Verb (Transitive) | Notes | Adjective | Verb (Intransitive) | Adverb | |------|------|-------------------|-------|-----------|---------------------|--------| | `bup` | plant, vegetation, flora, fungus, weed, herb, vegetable | to bear, to cultivate, to grow, to produce, to put forth | Plants do this when they grow, but farmers also do this when they grow plants. | vegetative, green, botanical, leafy, floral, growing, blooming, vegetarian | to grow, to sprout, to bear fruit | naturally, adaptively, increasingly | | `pun` | speaker, author, communicator, writer, artist, performer, self, me, I, us, we | to say, to express, to communicate, to write, to tell, to show, to share | The object of `pun` is what's expressed. To name the audience, use a quote as the verb: `abi iau, maksi` (Abi talks to Max). | expressive, communicative, articulate | to speak, to talk, to communicate, to write | expressively, communicatively | | `poup` | help, helper, aid, assistance, rescuer, hero, expert, friend | to help, to aid, to assist, to save | | helpful, useful, necessary, needed, skilled | to help, to assist | helpfully, skillfully | | `paun` | symbol, definition, signal, sign, indicator, pointer, warning, flag, marker, message, expression, gesture, token, emblem, text, word, book, speech, story, song, performance | to stand for, to signify, to indicate, to symbolize, to represent, to point to, to flag, to define | | symbolic, indicative, representative, expressive, emblematic | to be a symbol | symbolically, expressively | | `pap` | group, family, town, community, clan, tribe | to love, to care for | | communal, collective, tribal, familial, loving | to love, to care, to coexist peacefully | communally, lovingly | | `baz` | link, connection, bond, chain, relationship | to link, to connect, to bind, to relate, to join | When used as a verb, this does not automatically imply that the subject, which is responsible for the binding, is also bound. | linked, connected, bonded, relational, joint | to connect, to bond | connectedly, jointly | | `paps` | cooperation, coordination, civilization, harmony, synergy, teamwork, collaboration, organization | to coordinate with, to cooperate with, to harmonize with, to sync with, to organize, to collaborate with, to align with | | coordinated, cooperative, harmonious, synchronized, organized, collaborative, aligned, unified, concerted, utopian | to cooperate, to collaborate, to coordinate, to sync | cooperatively, harmoniously, collaboratively | | `bik` | food, fuel, nourishment, sustenance, meal | to eat, to consume, to feed on, to refill on, to be nourished by | | edible, nutritious, tasty, energizing | to eat, to feed, to dine | nourishingly | | `pim` | parent, caretaker, steward, mentor, healer, nurse, gardener | to be cared for by, to be taken care of by, to be raised by, to be looked after by, to be loved by | | nurturing, caring, loving, watchful, supportive, gentle | to be cared for, to be raised, to be looked after, to be loved | gently, caringly, supportively | | `pip` | trade, exchange, barter, goods, product, service | to trade, to exchange, to barter, to buy, to sell, to deal in | | trading, exchanging, bartering, transactional, commercial, mercantile, reciprocal, interactive, traded | to trade, to barter, to deal | commercially, reciprocally | | `pif` | gift, donation, present | to give, to offer, to share, to send, to provide, to bestow, to grant, to hand over, to distribute, to set down, to place, to drop | | generous, charitable | to give, to share, to donate | generously, charitably | | `bih` | branch, wing, side, limb, organ, offshoot, department | to put out, to branch into, to extend | | branching, extended, spreading, divergent, specialized | to branch out, to spread, to diverge, to specialize | divergently |
`F` — Earth (Places, Substances, Things, Texture, Color, Flavor, Shape, Composition)| Word | Noun | Verb (Transitive) | Notes | Adjective | Verb (Intransitive) | Adverb | |------|------|-------------------|-------|-----------|---------------------|--------| | `vuf` | place, land, earth, ground, territory, terrain, location, area | to be the place of, to be near, to be around, to contain, to support | | embedded, planted, grounded, located | to be a place | regionally | | `fun` | origin, source, birthplace, homeland, native land, roots, inception, genesis, provenance, wellspring | to be the origin of, to be the source of, to give rise to, to produce, to generate, to spawn, to be the birthplace of, to be the home of | Provenance: where a thing comes from, whether or not anyone meant it to. Compare `tum`, deliberate making. | original, native, indigenous, ancestral, primordial, fundamental, seminal | to be a source, to be an origin | originally, fundamentally | | `fut` | container, vessel, pot, basket, barrel, bag, trunk, closet, cabinet, pantry, cargo hold | to have, to own, to possess, to hold, to contain, to carry, to lift, to bear, to support | | possessive, clingy, sticky, closed, sealed, holding | to bear weight | possessively, tightly | | `faf` | thing, something, stuff, substance | to be, to equal, to relate to, to be similar to | When used intransitively, there's an implication of some unspecified relation. Doesn't imply true existence in the same way as `suin`. | generic, typical, average, common, frequent, usual, nondescript, mundane | to be something, to do something | often, typically, generally | | `faip` | flavor, taste, smell, essence, chemical | to taste of, to have the chemistry of, to be flavored with | More about the substance itself, rather than any subjective experience. | flavorful, pungent, smelly, reactive, volatile, tasty | to be flavorful, to be flavored | chemically, tastily | | `vin` | here, now, this | to arrive at, to reach, to come to | | present, nearby, local | to be here, to be present, to arrive | here, now, locally, presently | | `feim` | mix, mixture, combination, blend | to mix, to combine, to blend, to interweave, to integrate | Like `baz`, this doesn't carry an implication, when used as a verb, that the subject is part of the mixture; it's the one doing the mixing. | mixed, blended, homogeneous, integrated | to mix something | mixedly, integrally | | `fik` | color, hue, tint, shade | to be the color of, to be colored like | | colorful, vibrant, vivid | to be colored, to wear a color, to be painted, to glow, to change color | vividly, vibrantly | | `fif` | pattern, design, shape, arrangement, sequence, order, axis | to arrange, to organize, to structure, to pose | | patterned, structured, orderly, regular, shaped, arranged, posed | to organize, to pose | structurally, regularly | | `fic` | part, piece, chunk, component, element, subset | to split, to disassemble, to deconstruct, to take apart, to break, to shatter | | split, some, broken, distinguished, fractured, divided | to split something, to divide something | deconstructively |
`H` — Air (Sky, Space, Freedom, Openness, Direction, Sound, Lines, Points, Softness)| Word | Noun | Verb (Transitive) | Notes | Adjective | Verb (Intransitive) | Adverb | |------|------|-------------------|-------|-----------|---------------------|--------| | `xuh` | sky, air, atmosphere, breeze, wind, heights, void, gap, space, heaven | to be above, to fly over, to hover over | | high, aerial, lofty, skyward, airborne, up, above, over, atop | to fly, to soar, to rise, to float | aerially | | `hun` | key, opener | to open, to uncover, to reveal, to expose, to spread, to unseal, to unblock, to unlock, to free, to unfasten | | open, uncovered, revealed, exposed, accessible, free, approachable, available, receptive, welcoming | to be a key | openly, accessibly, receptively | | `hout` | top, peak, summit, head, crown, pinnacle | to be at the top of, to oversee, to be the upper edge of | Used both literally (mountain peaks) and metaphorically (monarchs). | top, highest, uppermost, paramount, crowning | to be at the top | primarily | | `huf` | fluff, cloud, wool, mist, haze, foam | to envelop, to hug, to smother, to snuggle, to cuddle, to wrap, to pad | | cloudy, fluffy, misty, hazy, obscure, enveloping, snuggly, soft, cozy | to snuggle, to cuddle | softly, cozily | | `has` | elongation, extension, length, line, stick, shaft, tube, pipe, rod, staff, tendril, ribbon, stripe | to extend, to elongate, to span, to draw out, to stretch, to reach | | elongated, extended, lengthy, linear, reaching, spanning, stretched, narrow, slim, slender, threadlike, continuous, connected | to stretch, to extend, to reach out | lengthily, continuously | | `xarc` | smoke, vapor, steam, gas, smog | to fade into, to disappear into, to evaporate into, to dissipate into | | fading, vanishing, ephemeral, transient, fleeting, temporary, ghostly, faint, transparent, dispersed, scattered, diluted | to vanish, to evaporate, to dissipate, to disperse, to thin out, to become transparent, to lose form, to diminish gradually, to sublimate, to atomize, to boil | faintly, gradually, fleetingly | | `hait` | shrimp | to shrink, to reduce, to minimize, to lessen, to diminish | To express "X is small", we place X as the object in a sentence with this as the verb, usually leaving off the subject. | small, tiny, minor, slight, petite, reduced, shrunk, minimized, lessened, diminished | to make it smaller | slightly, minimally | | `hik` | sound, noise, tone, vibration | to sound like | | audible, sonic, resonant, loud | to sound, to make noise, to resonate, to echo | audibly, loudly, resonantly | | `hif` | direction, way, path, route, method, manner | to navigate, to follow, to take, to go by | | directional, aligned, oriented | to navigate, to orient, to find the way | directionally | | `xih` | option, opportunity, possibility, path, choice | to be able to, to have the opportunity to, to be free to | | free, liberated, unbound, unchained, empowered, unrestricted, available, unimpeded, autonomous, self-determined, independent, flexible, loose, uncontrolled, unrestrained | to be free, to be able, to have opportunity | freely |
`C` — Shadow (Negation, Conflict, Evil, Darkness, Death, Disease, Collapse, Cold, Ice, Mystery, Strangeness)| Word | Noun | Verb (Transitive) | Notes | Adjective | Verb (Intransitive) | Adverb | |------|------|-------------------|-------|-----------|---------------------|--------| | `juc` | opposite, negation, negative, reversal | to negate, to deny, to refute, to oppose, to contradict, to be the opposite of | English "invert" can mean `juc` or `jamz`. `juc` is like an intrinsic opposite. Evil twins, negative numbers, and most opposites are `juc`. By contrast, `jamz` is about something going topsy-turvy and becoming the other way around without being a different type of thing, such as when an underdog takes the lead, when a mystery novel takes a turn, or when a shirt is inside-out. | negative, inverted | to be negative, to be oppositional | negatively | | `cun` | demon, evil, villain | to harm, to worsen, to damage, to hurt, to spoil, to ruin | | harmful, detrimental, hurtful, negative, damaging, bad, malevolent, dangerous, destructive, undesired, unwanted | to be bad, to do harm | harmfully, destructively | | `cuk` | coldness, chill, coolness, frost, yin, ice | to cool, to chill, to freeze, to numb, to deaden, to slow | | cold, chilly, frigid, icy, frosty, cool, sluggish, slow, muted, quiet, unresponsive, passive, calm, patient | to be cold, to chill, to freeze | slowly, calmly, patiently | | `cus` | oddity, anomaly, stranger | to differ from, to deviate from, to stand out from | | odd, strange, unusual, irregular, peculiar, eccentric, abnormal | to stand out, to differ, to deviate | oddly, peculiarly, unusually | | `coum` | germ, bug, disease, pathogen, microbe, bacteria, virus, parasite | to infect, to contaminate, to steal from, to mooch off | | gross, harmful, diseased, unclean, toxic, yucky | to be infectious, to steal | toxically, parasitically | | `jamz` | twist, turn, reversal, flip, inversion, about-face, U-turn, upset, turnabout | to twist, to turn around, to reverse, to flip, to invert, to overturn, to upend, to subvert, to wring | Where `juc` is the static fact of being opposite, `jamz` is the act of becoming opposite: a discontinuous flip from one state to its reverse. Where `maz` curves smoothly and comes back around (loops, cycles, returns), `jamz` turns abruptly and stays turned. Like with `taz`, there's an implication that the subject performing this verb also gets flipped in the process of flipping the object. | twisted, twisting, reversed, flipped, inverted, upside-down, contrary, topsy-turvy | to twist, to turn around, to flip | conversely, contrarily, in reverse, the other way around | | `cak` | sexual abuse, rape, profanity | to fuck, to rape, to sexually use, to violently take, to despoil, to ravage, to violate, to pillage, to desecrate, to molest | All with sexual connotations | abusive, violating, desecrating | to fuck, to rape, to pillage | violently, unilaterally, abusively | | `cap` | fight, battle, conflict, struggle, war | to fight, to combat, to resist, to oppose | | violent, aggressive, combative, confrontational | to fight, to struggle | violently, aggressively, confrontationally | | `cin` | mystery, uncertainty, possibility, enigma, unknown | to cover, to obscure, to hide, to conceal, to cloud | | mysterious, uncertain, possible, enigmatic, obscured, hidden, veiled | to be a mask | mysteriously, enigmatically | | `cif` | trash, waste, garbage, poop | to lose, to misplace, to waste, to forget, to drop, to discard, to abandon | | wasted, trashed, spoiled, forgotten, abandoned | to waste something, to lose something, to poop | wastefully | | `ciz` | falsehood, no | to disprove, to deny | Also used as a negative response to yes/no questions | false, inaccurate | to disprove it, to deny it | falsely, inaccurately | | `ceim` | victim, corpse, death, mortality | to kill, to slay, to break, to end, to murder, to delete | | dead, deceased, lifeless, inert, broken, still, gone, departed, terminal | to kill | mortally |
`S` — Time (Abstraction, Quantity, Infinity)| Word | Noun | Verb (Transitive) | Notes | Adjective | Verb (Intransitive) | Adverb | |------|------|-------------------|-------|-----------|---------------------|--------| | `zus` | time, period, duration, era, event, moment, instant | to be the time of, to be the duration of | | temporal, chronological | to last, to take time | temporally, lengthily | | `suat` | multitude, plethora, abundance, crowd, swarm | to multiply by, to be abundant in | | many, plentiful, numerous, multiple, abundant, manifold | to abound, to throng, to multiply | abundantly | | `suaf` | infinity, eternity | to perpetuate, to extend, to continue | | infinite, boundless, unending, limitless, eternal, continual | to continue, to go on, to last forever | eternally, infinitely, endlessly | | `suin` | anything, instance, example, possibility, existence | to be an instance of, to be an example of | | existing, possible, real, any | to exist, to occur, to be possible, to happen at any time | ever, anytime, anywhere | | `zuip` | everything, universe, all, rule, law, total, totality | to encompass, to include, to rule, to enforce, to imply | How things must be. Compare `kud`, stemming from a choice. | universal, omnipresent, whole, complete, total | to rule, to be universal | universally, completely, totally | | `zous` | past, ancestor, elder, predecessor | to precede, to come before, to be lesser than | | former, prior, historical, outdated, elderly, old, ancient, expired, bygone, preceding, early | to come before, to have been | before, earlier, previously, ago | | `zouc` | future, descendant, successor | to come after, to follow, to be greater than | While it's useful to have a parallel word to `zous` for various reasons (mostly mathematical), there's a broad preference for `zeif` to talk about the future, such as when augmenting verbs to convey tense. | latter, post, late, new | to come after | after | | `sis` | addition, extra, supplement, bonus, encore, addendum, appendix | to append, to supplement with, to grow by, to increase by, to extend by | The subject is what grows, and the object is the growth that gets added on. | additional, extra, supplementary, additive, added, further, more | to add, to increase | also, too, as well, in addition, moreover, besides, furthermore | | `sip` | assembly, collection, congregation, cluster, aggregation, gathering, combination, set | to gather, to assemble, to cluster, to aggregate, to associate, to combine | | collective, communal, assembled, aggregated, bunched, plural | to shepherd, to gather | together, collectively | | `sih` | list, enumeration, series, sequence, array, ranking, ordering, count, line | to index into, to select from (a list), to enumerate, to list, to itemize, to order, to rank, to organize | An index counts the entries before it, like an ordinal (see Cardinals and Ordinals), so `crif sih` selects the sixth entry. | ranked, listing, enumerating, cataloging, sequential, ordered, orderly, countable | to count, to select | sequentially, methodically | | `zeif` | fate, destiny, vision, prediction, outcome, prophecy | to foretell, to foresee, to predict, to anticipate, to expect | | predicted, anticipated, inevitable, certain, assured, destined, fated, preordained, predestined, future, determined, confident | to foresee, to forecast | inevitably, certainly, confidently |
We have so far been looking only at the single-syllable "root words" that are the heart and soul of Papsa. But there is, of course, a huge range of "compound words" that are made by combining the root words in various ways. For the sake of keeping this document readable, compound words have been relegated to the appendix.
const coreWordEntry = (wrd, row) => ({
word: wrd,
intransitiveMeaning: row['Verb (Intransitive)'],
transitiveMeaning: row['Verb (Transitive)'],
nounMeaning: row['Noun'],
adjectiveMeaning: row['Adjective'],
adverbMeaning: row['Adverb'],
notes: row['Notes'],
});
// Papsa has no homonyms, so no two core words may share their base letters. A collision is
// reported in the validation section (keeping the first entry) instead of silently overwriting it.
const addCoreWord = (wrd, row, where) => {
const key = baseLetters(wrd);
if (coreWords[key]) tableErrors.push({ where, reason: `${wrd} has the same base letters as ${coreWords[key].word}` });
else coreWords[key] = coreWordEntry(wrd, row);
};
const rootWordTables = [
baseAnaMeanings, baseAmaMeanings, baseAkaMeanings, baseAtaMeanings,
baseApaMeanings, baseAfaMeanings, baseAhaMeanings, baseAcaMeanings, baseAsaMeanings,
];
for (const table of rootWordTables) {
for (const [wrd, row] of table["Word"]) addCoreWord(wrd, row, 'root words');
}
// Single unified lookup across both dictionaries.
const lookupWord = (wrd) => {
const key = baseLetters(wrd);
return grammarWords[key] ?? coreWords[key];
};
The core unit of a Papsa expression is the clause. Each clause has three slots — subject, verb, object — which, in the absence of grammar words, are filled by content in that order. All slots are optional, and can take multiple pieces of content. When the object (or subject) is elided, the meaning of what remains doesn't change. If more content is provided after the object, it defaults to being another object.
nup | There was a person. nup tum guk | People make fire. tum guk nup | The maker burns people. nup guk | The food was hot enough to burn someone. abi bik bik | Abi will eat the food.
const mainContentParsers = [(ctx, inp, out) => {
if (inp[0] && inp[0]?.type !== 'grammar')
return out.push({ ...inp.shift(), slot: ctx.defaultSlot }); // Always truthy.
}];
const miscClauseParsers = []; // Parts of clauses that aren't content also exist. See following sections.
const parseContent = (ctx, defaultSlot, inp, out) =>
ctx.contentParsers.some(p => p({...ctx, defaultSlot}, inp, out));
const getNextSlot = o => o ? o.nextSlot ?? (o.slot === 'S' ? 'V' : 'O') : 'S';
const parseClause = (ctx, inp, out=[]) => {
while (parseContent(ctx, getNextSlot(out.at(-1)), inp, out)
|| miscClauseParsers.some(p => p(ctx, inp, out))) {}
return out;
};
A phrase is a piece of content made of multiple words. The most common kind
of phrase is an augmentation, which takes a base and an augment (both of which
are content). When used in a noun slot we usually think of the augment as an adjective, and for
verbs we think of the augment as an adverb. The simplest way to augment is using the word
`ei` as an afterthought, with the base on the left and the augment on the right.
bik ei suk guk | Two foods burn. bik guk ei suk | Food burns twice.
Augmentation can also be done with a forethought grammar word, used before the content. These
grammar words explicitly encode what slot the resulting phrase goes in, and how many augments are
attached to the base. As with `ei`, the base comes first, and the augments after.
For instance, the word `am` marks a verb with one adverb, while `uk`
indicates a subject with two adjectives. Because these words indicate slots, they can be used to
rearrange clauses and break the standard word order. When the word order is changed, content that
follows will default to the next slot in the order.
bik am guk suk | Food burns twice. uk bik bup sat guk | Three vegetarian foods burn. am guk suk uk bik bup sat | Three vegetarian foods burn twice.
The second most common kind of phrase is an association, or alternatively
called "possession." Association has the same general grammatical form as augmentation, but uses
the afterthought word `ui`, and a different set of forethought words. It is typical
to use the "n" forethought associators ("no associated content") to explicitly mark slots
(especially "na" for the verb) when rearranging the sentence or simply to add clarity. There are
two special words `uip` and `uin` which are shorthand for
`ui pun` and `ui nuh` — "my"
and "your", respectively.
guk ui nup | The person's fire guk ui nup bik bup | The person's fire eats the plant. nau bik | There's food. na guk bik | The food burned. na tait bup | The plant is big. guk uin guih | Your fire glows. kau bik guk pun guih | The food of the fire, which is also mine, is glowing.
There is one last way to make a phrase: grouping. It has a similar pattern as the other two, using its own set of words. See the Lexicon for the specific list.
guk ou nup bik bup | The fire and the person ate the plant.
// Forethought
mainContentParsers.push((ctx, inp, out) => {
if (inp[0]?.type === 'grammar' && inp[0]?.phraseSize) {
const w = inp.shift();
const phraseContent = [];
while (phraseContent.length < w.phraseSize) {
if ( ! parseContent(ctx,w.slot,inp,phraseContent)) // Fail if we get less than expected.
fail(`${w.word} requires ${w.phraseSize} content items; `+`found ${phraseContent.length}.`);
}
return out.push({ ...w, phraseContent });
}
});
// Afterthought
mainContentParsers.push((ctx, inp, out) => {
if (inp[0]?.type === 'grammar' && inp[0]?.phraseMode && ! inp[0]?.phraseSize) {
const w = inp.shift();
const phraseContent = [out.pop() ?? fail("Afterthought phrases need preceding content")];
const slot = phraseContent[0].slot;
parseContent(ctx, slot, inp, phraseContent) || fail("Afterthought phrases need following content");
return out.push({ ...w, slot, phraseContent });
}
});
// Mine/Yours Phrase
mainContentParsers.push((_ctx, inp, out) => {
if (inp[0]?.word === 'uip' || inp[0]?.word === 'uin') {
const base = out.pop() ?? fail(inp[0].word+" needs preceding content");
return out.push({ ...inp.shift(), slot: base.slot, phraseContent: [base] });
}
});
A clause can also be used as content that fills a slot in an outer clause. This is done by
wrapping the clause in marks that indicate what slot it is being used in based on their vowel.
("u" = Subject, "a" = Verb, "i" = Object.) Clause markers (either opening or closing) can be
elided if there's no ambiguity. Here, the word `ul` is used to indicate that the
text to the left is in a subordinate clause that's used as a subject.
pun naun | The speaker is good. pun ul naun | Speaking is good. nup pun ul naun | People speaking is good. nup pun ul vin ul naun | The fact that people speak here is good.
Here's a more complex example:
babi pun li lu bik ul naun | Bobby said that eating is good.
Breaking it down:
`babi pun` — Bobby said (subject + verb)`li` — opens object-slot clause`lu bik ul` — subject-slot clause within: "eating" / "the existence of food"`naun` — verb of the object clause: is good`li` with elided `il`)
// A clause mark without a slot of its own (a parenthesis, see Numerals and Symbols) becomes the one for the slot it's in.
const clauseMark = (tok, slot) => {
if (tok.slot) return tok;
const v = { S: 'u', V: 'a', O: 'i' }[slot];
return { ...tok, ...makeToken(tok.clause === 'open' ? 'l' + v : v + 'l'), rawTok: tok.rawTok };
};
// Forethought Clause
mainContentParsers.push((ctx, inp, out) => {
if (inp[0]?.clause === 'open') {
const w = clauseMark(inp.shift(), ctx.defaultSlot);
const clauseContent = parseClause({...ctx, ignoreAftClause: w.slot}, inp);
// We parse ignoring the matching closer, so now we need to consume it, if present.
const closer = inp[0]?.clause === 'close' && (inp[0].slot ?? w.slot) === w.slot ? clauseMark(inp.shift(), w.slot) : undefined;
return out.push({ ...w, clauseContent, ...(closer && { closer }) });
}
});
// Afterthought Clause (opener elided)
mainContentParsers.push((ctx, inp, out) => {
const closesOpener = ctx.ignoreAftClause && (inp[0]?.slot ?? ctx.ignoreAftClause) === ctx.ignoreAftClause;
if (inp[0]?.clause === 'close' && ! closesOpener) {
const clauseContent = out.splice(0);
if (clauseContent.length < 1) fail("Afterthought clauses can't be empty.");
return out.push({ ...clauseMark(inp.shift(), clauseContent[0].slot), clauseContent });
}
});
Much like how Papsa words can be used as nouns or as verbs, they can also be used as
prepositions. Consider the word `xuh`, which often is translated as "sky" or "fly,"
but can also be used as a verb to generally indicate that the subject is above some object.
Using the grammar word `θu`, we can turn the following `xuh` into a
preposition that indicates that the entire action of the clause is happening over something. If
we want to specify what it's above, we simply give that content immediately after.
ina neimguih | Ina watches. ina neimguih θu xuh | Ina watches from above. ina neimguih θu xuh guk | Ina watches from above the fire.
In this way, we see `θu` as making a side-statement that treats the entire action
as the subject, the content after it as the verb, and then (optionally) the object that the
clause is relating to via that verb. As a result of this flexibility, Papsa arguably has
thousands of prepositions, though in practice only a few dozen get used in everyday speech, and
most are effectively untranslatable, and must be creatively adapted to capture the gist.
abi pun θu kuh maksi | Abi speaks towards Max. abi pun θu naun maksi | Abi speaks for Max's benefit. abi pun θu guk maksi | Abi speaks, and that burns Max.
The word `tci` is exactly like `θu`, but flips the relationship.
The body of the clause is taken to be the object of the verb, and the additional content is the
subject.
abi pun tci kuh ardjantina | Abi speaks from Argentina. abi pun tci naun maksi | Abi speaks, thanks to Max. abi pun tci fut | Abi is speaking inside.
A clause can be given any number of prepositions by simply using multiple preposition markers.
If one wishes to give normal content after a preposition, the words `untc`,
`antc`, and `intc` can be used to explicitly close a preposition, with
the vowel indicating whether the next slot is a subject, verb, or object. To nest prepositions,
repeat the preposition mark for each level of depth.
abi pun tci fut θu xuh maksi | Abi is speaking inside, over Max. mainup nat θu vuf mumkuh θu θu xujuc vufdut θu θu θu fic vufmumtait | The boy played near the river that descended from the mountains that split the continent.
const parsePreposition = (ctx, inp, out) => {
if (inp[0]?.word === 'θu' || inp[0]?.word === 'tci') {
for (let i = 0; i < ctx.prepDepth; i++)
if (inp[0].word !== inp[1+i]?.word) return; // Only parse if prepDepth matches.
const w = inp.splice(0, 1+ctx.prepDepth)[0]; // Duplicated prep markers are dropped.
const prepContent = [];
parseContent(ctx, 'V', inp, prepContent) || fail(w.word+" must be followed by content.");
while (parseContent(ctx, w.word === 'θu' ? 'O' : 'S', inp, prepContent)) {}
while (parsePreposition({...ctx, prepDepth: ctx.prepDepth+1}, inp, prepContent)) {}
const closer = /^[uai]ntc$/.test(inp[0]?.word) ? inp.shift() : undefined;
return out.push({ ...w, prepContent, ...(closer && { closer, nextSlot: VOWEL_SLOT[closer.word[0]] }) });
}
}
miscClauseParsers.push(parsePreposition);
To end a sentence and start a new one, Papsa speakers use the word `.`, which
sounds like a "tsk!" tooth-sucking sound. Papsa prefers short sentences, rather than having
complex conjunctions, but provides the words `uas` and `iur`, which go
at the start of a sentence, and indicate that it relates to the previous statement either as a
natural continuation or as a contrast/counterpoint, a bit like "and" and "but" in English.
pun naun. nuh cun | I am good. You are bad. nuh bik. iur pun minbik | You ate, but I am hungry. bup guk. uas ceim ei vin | The plant burned, and now it's dead.
When someone who has been speaking wants to signal that they are done speaking, or when
someone who has been listening wants to start speaking, they can use `.` multiple
times, almost like a throat-clearing noise.
To mark a sentence as a polite request, put `ouei` at the start and end. (The
opening `ouei` comes after connectives like `iur`.) Only one
`ouei` is needed, and it's not uncommon for speakers to only give the
end-of-sentence marker, but this is seen as somewhat rude, and we usually translate such sentences
as commands. Use `ceiou` instead of `ouei` to prohibit.
nuh tum guk | You'll make a fire. (statement) ouei nuh tum guk ouei | Please make a fire. nuh tum guk ouei | Make a fire. ceiou nuh tum guk ceiou | Please don't make a fire. nuh tum guk ceiou | Don't make a fire.
In the same way that `ouei` marks a sentence as a command, the word
`sou` marks a sentence as a yes-or-no question. It typically only shows up at the
end of a sentence, but can technically be placed at the beginning.
nuh tum guk sou | Are you a fire maker?
It's common to answer such questions by repeating the verb, or by using the core words
`nis` and `ciz` for "yes" and "no", respectively.
Papsa does not have a grammatical distinction between what/why/when/how. To ask such a
question, the word `eis` is used (instead of `sou`) at the start of the
sentence, and then somewhere in the sentence the word `ous` is used as a placeholder
for the topic of inquiry.
eis nuh tum ous | What did you make? eis nuh muk ei mazguih ous | How are you doing today?
const ousParser = (ctx, inp, out) => {
if (inp[0]?.word === 'ous') return out.push({ ...inp.shift(), slot: ctx.defaultSlot });
};
const parseSentence = (inp, quoteDepth=0) => {
const r = {};
if (inp[0]?.word === 'uas' || inp[0]?.word === 'iur') r.connective = inp.shift();
if (['ouei', 'ceiou', 'sou', 'eis'].includes(inp[0]?.word)) r.mood = inp.shift();
const contentParsers = [...mainContentParsers, ...(r.mood?.word === 'eis' ? [ousParser] : [])];
r.clause = parseClause({quoteDepth, prepDepth: 0, contentParsers}, inp);
if (['ouei', 'ceiou', 'sou', 'eis'].includes(inp[0]?.word)) {
const m = inp.shift();
if (r.mood && r.mood.word !== m.word) fail("Mood mismatch.");
r.mood = m;
}
return r;
};
// A click token: "." between sentences, "," closing a quote, or "!", the click of the basic script.
const isClickTok = (t) => t.rawTok === '.' || t.rawTok === ',' || t.rawTok === '!';
const parseExpression = (inp, quoteDepth=0) => {
if (inp.length === 0) {
return [];
} else if (isClickTok(inp[0])) {
let i = inp.findIndex(t => ! isClickTok(t));
if (i === -1) i = inp.length;
if (i <= quoteDepth) return []; // Only parse if clicks exceed quote depth.
inp.splice(0,i);
return [{clicks: i - quoteDepth}, ...parseExpression(inp, quoteDepth)];
} else {
const before = inp.length;
const sentence = parseSentence(inp, quoteDepth);
if (inp.length === before)
fail(`Unparseable input: ${inp.map(t => t.rawTok).join(' ')}`);
return [sentence, ...parseExpression(inp, quoteDepth)];
}
};
To address a person, the word `tu` can be used (usually at the start or end of a
sentence), followed by that person's name.
punmain tu babi | Hello, Bobby pundut tu babi | Goodbye, Bobby.
The word `iau` is used to quote the text/speech that follows (normally Papsa) and
turn it into content. It is grammatical to explicitly close a quote by using a single click, which
is usually more emphasized than the normal sentence delimiter, and often pronounced with a
lateral or palatal click (like imitating a clock) instead of the dental "tsk," and written (for
English speakers) using a comma instead of a period. Within a quote, sentence delimiters get
repeated an additional time.
nuh pun iau nis, | You said "yes." na pun iau na pun nis ouei.. uas xih, θu naun. nuh niŋ sou | For goodness' sake, it's said "Speak the truth and there will be freedom". Do you understand?
Like any content, a quote can be used as a verb. As a verb, a quote relates the speaker (subject) to the audience (object). Closing a quote right after opening it leaves the words unsaid, which is a common way to say that someone talks to someone.
abi iau nis, maksi | Abi says "yes" to Max. abi iau, maksi | Abi talks to Max.
Foreign words are most typically embedded in Papsa by turning them into names. But when that's
not trivial, the word `uau` can be used to embed foreign speech and convert it to
Papsa content. Like with quotes, it is grammatical to always close foreign speech with the
emphasized click (","). Explicitly closing with a click is somewhat burdensome, however, in
certain contexts where it's clear what is and isn't Papsa. Thus, there is an alternative (and
more popular) word, `uai`, which has the same meaning, but warns the audience that
you're playing fast-and-loose and won't close the foreign speech explicitly.
suci naun kiti | Kitty likes sushi. pun uau ran around the city , tci zus uai all night | I ran around the city all night.
miscClauseParsers.push((ctx, inp, out) => {
if (inp[0]?.word === 'tu') {
const addressee = [];
return out.push({ ...inp.shift(), // Anchor on `tu`, then add addressee (or fail)
...((parseContent(ctx, 'O', inp, addressee) || fail("`tu` must be followed by content.")) && { addressee }),
nextSlot: getNextSlot(out.at(-1)) });
}
});
// Quote
mainContentParsers.push((ctx, inp, out) => {
if (inp[0]?.word === 'iau') {
const w = inp.shift();
const quote = parseExpression(inp, ctx.quoteDepth + 1);
// Unlike most places, quoted speech checks for the specific rawTok.
// This is arguably not a perfect reflection of the spec, but it makes errors much more obvious.
const closer = inp.shift();
if (closer?.rawTok !== ',') fail("All Papsa quotes must be explicitly closed.");
return out.push({ ...w, slot: ctx.defaultSlot, quote, closer });
}
});
// Explicitly-closed Foreign Speech
mainContentParsers.push((ctx, inp, out) => {
if (inp[0]?.word === 'uau') {
const w = inp.shift();
// Unlike most places, quoted speech checks for the specific rawTok.
// This is arguably not a perfect reflection of the spec, but it makes errors much more obvious.
const i = inp.findIndex(t => t.rawTok === ',') + 1 || fail("`uau` demands an explicit closer.");
const foreign = inp.splice(0,i), closer = foreign.pop(); // Split off the click.
return out.push({ ...w, slot: ctx.defaultSlot, foreign, closer });
}
});
// Implicitly-closed Foreign Speech
mainContentParsers.push((ctx, inp, out) => {
if (inp[0]?.word === 'uai') {
const w = inp.shift();
const i = inp.findIndex(t => ! t.probablyForeign);
return out.push({ ...w, slot: ctx.defaultSlot, foreign: i === -1 ? inp.splice(0) : inp.splice(0,i) });
}
});
Papsa's lack of specific grammatical structures or inflections for things like tense and plurality allows for expressions that are more naturally vague and contextual than in many languages. To explicitly note, and thus emphasize, things like possibility or timing, it is typical to augment the verb or provide a preposition. To note plurality and gender, it is common to augment the relevant noun.
tina kimcukcuc ei cin | Tina might be awake / might wake up. guk guih ei vin | The fire is glowing. guk guih ei zous | The fire was glowing. guk guih ei zeif | The fire will glow. pun bik ei am vin zous | I have already eaten. pun bik ei mazgu | I ate today / will eat today. pun bik ei mazgu ei zeif | I will eat later today. pun bik ei am mazgu zouc | I will eat tomorrow. pun bik ei vin ei maz | I'm eating again.
When multiple items fill the same clause slot (subject/verb/object), the statement becomes a claim that some selection from those items makes it true. In practice, listing multiple items usually implies that all of them are at least somewhat involved — even if only partially or in expectation — not merely that the listener may pick one arbitrarily. As with natural language generally, the literal and pragmatic readings can diverge.
To firmly claim that multiple parties were involved in an action, or that multiple actions
were definitely taken, the speaker can form a group, using words like `ou`.
nau pun nau nuh na tum guk | You and/or I will make a fire. pun nau nuh tum guk | You and/or I will make a fire. pun ou nuh tum guk | You and I will make a fire together.
Association (`ui`) names an additional entity that is relevant
to understanding the focal content. When both entities are inanimate, the most common translation
into English is to use the word "of" to join the things. When one person is involved, the
relationship is usually translated as ownership by the person, regardless of whether the person
is the base or the associate. When two people are involved, this is usually seen as a familial
connection.
bik ui bup | food of the plants (for plants, from plants, or just notably plant-adjacent) bup ui nup | the man's plant (plant is focal; man is context) nup ui bup | the man with the plant (man is focal; plant is context)
Augmentation (`ei`), by contrast, is conceptual fusion: the base
entity has the character of the augment, as though the two concepts were merged. The result
is usually rendered as an adjective or adverb in English.
bik ei bup | vegetarian food (food that has the character of plant) bik ei mum | wet food (food that has the character of water)
While augmented words and compound words using the same roots are often related, they are not interchangeable.
bik ui mum | food of the water (e.g. food intended to be eaten on a boat) bik ei mum | wet food (e.g. soggy cereal) bikmum | seafood (e.g. clams) bik ui bup | food of the plants (e.g. fertilizer) bik ei bup | vegetarian food (e.g. veggie pizza) bikbup | produce (e.g. apples)
Location is often indicated with a `vuf` preposition. With `tci vuf`,
the preposition content is the place where the action happens, so it's usually translated as "at"
or "in." With `θu vuf`, the main action of the sentence defines the place, and the
preposition content is secondary. Thus, it's often translated as "near" or "by."
ina nat tci vuf mumkuh | Ina plays in the river. ina nat θu vuf mumkuh | Ina plays by the river.
English is full of words like “this”, “that”, “here”, “there”, “thing”, and “stuff”, because it's inconvenient to spell out the subject of discussion each time one wants to reference it. Papsa's ability to elide arbitrary parts of speech, its natural vagueness, and its powerful pronoun system reduce the need, but don't totally eliminate it.
pun min vin | I want this. pun min vinjuc | I want that. vin naun | Here's good. vinjuc naun | There's good. faf naun | Things are good. mum faf faf | Water's made of stuff.
If there are multiple options, the most natural Papsa expression is to use numbers
(usually 0 and 1) instead of "this" or "that". When the `ous` placeholder is associated with
something, we interpret that thing as an option. To reference exclusive options outside the
context of a question, use `xih`.
eis nuh min kai ous soun saum | Do you want option #0 or #1?
nuh fut kai xih soun saum | You have options: #0 or #1.
When we augment a noun with a number, we think of the number as counting the noun. When we augment a verb, we think of the number as counting the number of times the action happens. To say that there's an absence, we simply use the number zero. This is how Papsa says "not": an action that happens zero times doesn't happen.
mauk ei sat kuh | Three animals went. mauk kuh ei sat | The animal went three times. nup ei soun | nobody maukmum naun ei soun | It doesn't like fish. na tait ei soun | It isn't big.
An absence isn't an opposite. To say that the opposite is the case, augment with
`juc` instead. (Augmenting with `ciz` would say that something about
the action is false, such as speaking a falsehood rather than staying silent.)
maukmum naun ei juc | Fish repels it. nuh pun ei soun | You're silent. nuh pun ei ciz | You're lying.
To express that something comes after some number of things in an ordering, we associate it with the negative of the number of things that came before. Rankings count down, since first place outranks second. This also leads to an off-by-one dynamic when translating between Papsa and languages like English. The "first" item is associated with zero, the "second" goes with negative one, and so on. See the appendix for more on numbers.
bik ui crat guk | The fourth food burned. bik guk ui crat | The food burned for the fourth time. nup ui croun | first people
You've made it through all the essential content. Congratulations! This section covers the last bits of code necessary to validate all the Papsa expressions on this page. This section and the appendices that follow are not essential to reading/writing/speaking Papsa, but may be helpful in learning, nonetheless.
const makeToken = (s) => {
let base, ixs;
try { base = baseLetters(s); ixs = wordIdxs(s); } catch {} // Non-letters (e.g. digits) leave these undefined.
for (const t of tokenParsers) {
try { const tok = t(s,base,ixs); if (tok) return tok; }
catch (e) {
// Parsers may stumble over non-letters (an undefined base) or throw a plain Error for a shape they
// reject. Anything else on real letters (a TypeError, say) is a bug in a parser, so say so.
if (base !== undefined && e.constructor !== Error) console.warn(`Token parser crashed on "${s}":`, e);
}
}
try {
if (wordType(ixs) === 'core') // Core-shaped but undefined: a not-yet-coined compound, a typo, or foreign.
return { rawTok: s, type: 'core', probablyForeign: true };
} catch {} // wordType can throw
return { rawTok: s, type: 'unknown', probablyForeign: true };
};
// "!" is the click of the basic script, so it splits off like "." and ",". So do the shorthand symbols (see
// Numerals and Symbols), which can stand for several words, and so several tokens.
const tokenize = (text) => {
const apart = ['.', ',', '!', ...Object.keys(mathSymbols['Character'])].map(c => '\\' + c).join('');
return (text.match(new RegExp(`[${apart}]|[^\\s${apart}]+`, 'g')) ?? []).flatMap(makeToken);
};
// Skips .foreign (deliberate non-Papsa); recurses .quote (which is Papsa).
const lintExpr = (expr, found) => {
for (const item of expr) if (item?.clause) item.clause.forEach(c => lintContent(c, found));
};
const lintContent = (c, found) => {
if (!c || typeof c !== 'object') return;
const isLeaf = c.rawTok !== undefined && c.type !== undefined &&
!c.clauseContent && !c.phraseContent && !c.prepContent && !c.quote && !c.foreign && !c.addressee;
if (isLeaf) {
if (c.type === 'unknown' || (c.probablyForeign && c.type !== 'name' && c.type !== 'pronoun')) found.push(c);
return;
}
for (const k of ['phraseContent','clauseContent','prepContent','addressee']) c[k]?.forEach(x => lintContent(x, found));
if (c.quote) lintExpr(c.quote, found);
};
neim kud. pif xarc sou | The feeling is compelling. Will the gift disappear? (Papsa's canonical pangram. Has the letters almost in order.) tait | It's big. naun | That's good. na tait dut | Stone is heavy. tait nuh | Giants listen. pun vin | I am here. guk figuk | The fire is red. pun guk bik | I burn the food. bik guk pun | The food burns me. nuh bik bup ei tait | You ate a big plant. tut fikzuip im huh guih | The stone was as yellow as the glowing sky. xufaf naun ei vin | The weather is currently good. coumas ei minbiguk | There was a starving caterpillar. mum naun maukmum | Water benefits fish. kana | There's a dog. kana bik bikmauk | Dogs eat meat. bikmauk naun kana | The dog likes meat. am kana tait | There is (or was) a big dog. am kana naun | There's a good dog. ak minbik fin hih | It might be hungry now. um kana tait naun pun | We like big dogs. maukmum naun fila sou | Does the cat like fish? fila bik ei am mazgu zous maukmum. na bik ei mazgu bikmauk. na bik ei am mazgu zouc mura | Yesterday, the cat ate fish. Today, it is eating meat. Tomorrow, it will eat mice. pun neimguih nuh | I see you. eis nuh tif ous | Where do you live? pun am pun naun papsa | I can speak Papsa well. mum naun im fila tait. iur bikmauk naunais im kana hait | The big cat likes water, but the small dog prefers meat. bikmauk naun kana. uas maukmum naunais fila | The dog likes meat, and the cat prefers fish. maukmum ui fila zouctait bikmauk ui kana | The cat's fish is bigger than the dog's meat. mauk ei suk neimnaun | Both animals are happy. xumum ei am mazgu zouc ul zuip li fila am kuh soun tci fut | If it rains tomorrow, the cat will stay inside. am xumum soun θu zus mazgu ei sat | It hasn't rained for three days. kana pun li na min li bik. iur am muf soun bik | The dog said that it wanted to eat, but it couldn't find any food. ouei mazguimain zuip li na pif bik tci muf fila ouei | Please feed the cat every morning. ceiou cif li ak pif suk mazguih mum ceiou | Don't forget to give it water twice a day. ciz. maukmum naun ei soun | No, it doesn't like fish. eis ous kut li um fila hait am naun soun im kana tait | Why doesn't the big dog like small cats? um mauk tait naunais ul kut | Because it prefers large animals. nuh am neimguih soun pun | You don't see me. am neimnaun soun im fila hait | They don't like small cats. eis ous paunup nuh | What is your name? eis ous zusmuk nuh | How old are you? guk guih ei guih | The fire shines brightly. guk ei guih guih | The bright fire glows. guixuvina guih ei vin | The sun is shining. ina guih | The sun is shining. guixuvina kuxuh ei vin | The sun is rising now. nup ei zuip puniguk | All the people shouted. nup ei sipfic puniguk | Some of the people shouted. nup ei suat am puniguk suk | Many of the people shouted twice. nup ei neimnaun am puniguk faf | Happy people often shout. mauk kuxuh | The animal jumped. fila kuxuh | The cat jumped up. fila ei main kuxuh tautaz | The kitten jumped onto the table. uk fila main hait uip vinjuc | My little kitten walked away. xumum | It's raining. xumum xujuc | The rain came down. fila ei main nat tci xumum | The kitten is playing in the rain. xumum ceim | The rain has stopped. xumum ceim ei zeivin | Soon the rain will stop. pun min li xumum ceim ei zeivin | I hope the rain stops soon. mauk ei mauk ak tif saum zous vin | Once wild animals lived here. am neimguih cuk θu maz | Slowly she looked around. ukfu neimguih ei cuk | Slowly she looked around. vinjuc ouei | Go away! pun kuh ouei | Let's go! kuh ouei | Let's go! nuh kuh ul naun | You should go. pun kuh ul naun pun | I'll be happy to go. am vin zeivin | He will arrive soon. ukpi kuvin ei zeivin | He will arrive soon. natikmaz ui mainupmain kumaz vinjuc | The baby's ball has rolled away. mainup ei suk kimtum ei sip | The two boys are working together. huvin ceim il na zeific | This mist will probably clear away. bupfik ei naun bup tci vuf zuip | Lovely flowers are growing everywhere. punuh bik ei am cuk sis ul naun | We should eat more slowly. nuh kuvin ei zouscun | You have come too soon. nuh kan li tumpaun ei fif il ul naun | You must write more neatly. vuf ei naun am vuf taz juc | Directly opposite stands a wonderful place. kana ui henri minif | Henry's dog is lost. fila uip fikcuc | My cat is black. na fic tiknup ui mainup | The little girl's doll is broken. am fif faf li pun kimcuk ei taut | I usually sleep soundly. mainup kukuh θu kuh djaki | The children ran after Jack. pun xih li nat θu zouc zusnunuh | I can play after school. pun kuh vuftif θu min neimtif | We went to the village for a visit. pun kuh mumkuh | We arrived at the river. pun am kat am vin zous nuh | I have been waiting for you. nuptifarc maz ei aihat guk | The campers sat around the fire. mainup ui um fila hait aihat θu vuf pun | A little girl with a kitten sat near me. mainup kat papa tci vuf tautun | The child waited at the door for her papa. um mainup zousnuif ui vuftif cif ei am mazgu zous im fila hait | Yesterday the oldest girl in the village lost her kitten. na main nuh tci vuf vuftif ei vin sou | Were you born in this village? pabih uin am kimneim naun sou | Can your brother dance well? nup vinjuc sou | Did the man leave? pabih uin kuvin θu min nuh sou | Is your sister coming for you? nuh kuh ei am saum mazgu sou | Are you going tomorrow? nuh kuh ei am saum mazgu sou | Can you come tomorrow? nuh xih li kuh ei am saum mazgu sou | Can you come tomorrow? nupvuf vinjuc θu zus mazcuk sou | Have the neighbors gone away for the winter? aritaku hikneim tci xumum sou | Does the robin sing in the rain? nuh kuh uip zusik sou | Are you going with us to the concert? nuh am kuh suin vufbupmuk sou | Have you ever travelled in the jungle? pun kumum mumkuzeif θu nais aisin ei suat | We sailed down the river for several miles. nupzuip niŋ li minmauk | Everybody knows about hunting. pun kumain vufdut tci zus mazguimain ei xuguih θu zouc zusguidut | On a sunny morning after the solstice we started for the mountains. tami neimfic pifcin ui primata | Tom laughed at the monkey's tricks. nupzous ui mau hasbup kuh θu vuf dutaut | An old man with a walking stick stood beside the fence. bubih ei xujuc cin tif ui skiuru | The squirrel's nest was hidden by drooping boughs. um mainbup hait am kat cuk im ina guk ui mazuh tci xuh xucuk | The little seeds waited patiently under the snow for the warm spring sun. mainup ei suat ui um houtikmaz bupfik na kimneim θu maz guktait | Many little girls with wreaths of flowers on their heads danced around the bonfire. houtik ui futbup xujuc taztaut | The cover of the basket fell to the floor. mainup ui croun ui sih dut tci vuf tautun | The first boy in the line stopped at the entrance. nupzous ei niŋ tif tifait ei hait θu hout vufmaz | On the top of the hill in a little hut lived a wise old woman. pun tif vufbikvuf ul zus il pun am kuneim faf vufbikmauk | During our residence in the country we often walked in the pastures. eis ous zus il nuptifcus ui vuftiftait uin kuvin | When will your guests from the city arrive? am jamz ifuh mumkuh θu vuf mumkuzeif ei tait | Near the mouth of the river, its course turns sharply towards the East. TODO: --- SET UP AN AUTOMATED TRANSLATION PIPELINE FOR THESE --- Between the two lofty mountains lay a fertile valley. Among the wheat grew tall red poppies. The strong roots of the oak trees were torn from the ground. The sun looked down through the branches upon the children at play. The west wind blew across my face like a friendly caress. The spool of thread rolled across the floor. A box of growing plants stood in the window. I am very happy. These oranges are juicy. Sea water is salty. The streets are full of people. Sugar tastes sweet. The fire feels hot. The little girl seemed lonely. The little boy's father had once been a sailor. I have lost my blanket. A robin has built his nest in the apple tree. At noon we ate our lunch by the roadside. Mr. Jones made a knife for his little boy. Their voices sound very happy. Is today Monday? Have all the leaves fallen from the tree? Will you be ready on time? Will you send this message for me? Are you waiting for me? Is this the first kitten of the litter? Are these shoes too big for you? How wide is the River? Listen. Sit here by me. Keep this secret until tomorrow. Come with us. Bring your friends with you. Be careful. Have some tea. Pip and his dog were great friends. John and Elizabeth are brother and sister. You and I will go together. They opened all the doors and windows. He is small, but strong. Is this tree an oak or a maple? Does the sky look blue or gray? Come with your father or mother. I am tired, but very happy. He played a tune on his wonderful flute. Toward the end of August the days grow much shorter. A company of soldiers marched over the hill and across the meadow. The first part of the story is very interesting. The crow dropped some pebbles into the pitcher and raised the water to the brim. The baby clapped her hands and laughed in glee. Stop your game and be quiet. The sound of the drums grew louder and louder. Do you like summer or winter better? That boy will have a wonderful trip. They popped corn, and then sat around the fire and ate it. They won the first two games, but lost the last one. Take this note, carry it to your mother; and wait for an answer. I awoke early, dressed hastily, and went down to breakfast. Aha! I have caught you! This string is too short! Oh, dear! the wind has blown my hat away! Alas! that news is sad indeed! Whew! that cold wind freezes my nose! Are you warm enough now? They heard the warning too late. We are a brave people, and love our country. All the children came except Mary. Jack seized a handful of pebbles and threw them into the lake. This cottage stood on a low hill, at some distance from the village. On a fine summer evening, the two old people were sitting outside the door of their cottage. Our bird's name is Jacko. The river knows the way to the sea. The boat sails away, like a bird on the wing. They looked cautiously about, but saw nothing. The little house had three rooms, a sitting room, a bedroom, and a tiny kitchen. We visited my uncle's village, the largest village in the world. We learn something new each day. The market begins five minutes earlier this week. Did you find the distance too great? Hurry, children. Madam, I will obey your command. Here under this tree they gave their guests a splendid feast. In winter I get up at night, and dress by yellow candlelight. Tell the last part of that story again. Be quick or you will be too late. Will you go with us or wait here? She was always shabby, often ragged, and on cold days very uncomfortable. Think first and then act. I stood, a little mite of a girl, upon a chair by the window, and watched the falling snowflakes. Show the guests these shells, my son, and tell them their strange history. Be satisfied with nothing but your best. We consider them our faithful friends. We will make this place our home. The squirrels make their nests warm and snug with soft moss and leaves. The little girl made the doll's dress herself. I hurt myself. She was talking to herself. He proved himself trustworthy. We could see ourselves in the water. Do it yourself. I feel ashamed of myself. Sit here by yourself. The dress of the little princess was embroidered with roses, the national flower of the Country. They wore red caps, the symbol of liberty. With him as our protector, we fear no danger. All her finery, lace, ribbons, and feathers, was packed away in a trunk. Light he thought her, like a feather. Every spring and fall our cousins pay us a long visit. In our climate the grass remains green all winter. The boy who brought the book has gone. These are the flowers that you ordered. I have lost the book that you gave me. The fisherman who owned the boat now demanded payment. Come when you are called. I shall stay at home if it rains. When he saw me, he stopped. Do not laugh at me because I seem so absent minded. I shall lend you the books that you need. Come early next Monday if you can. If you come early, wait in the hall. I had a younger brother whose name was Antonio. Gnomes are little men who live under the ground. He is loved by everybody, because he has a gentle disposition. Hold the horse while I run and get my cap. I have found the ring I lost. Play and I will sing. That is the funniest story I ever heard. She is taller than her brother. They are no wiser than we. Light travels faster than sound. We have more time than they. She has more friends than enemies. He was very poor, and with his wife and five children lived in a little low cabin of logs and stones. When the wind blew, the traveler wrapped his mantle more closely around him. I am sure that we can go. We went back to the place where we saw the roses. "This tree is fifty feet high," said the gardener. I think that this train leaves five minutes earlier today. My opinion is that the governor will grant him a pardon. Why he has left the city is a mystery. The house stands where three roads meet. He has far more money than brains. Evidently that gate is never opened, for the long grass and the great hemlocks grow close against it. I met a little cottage girl; she was eight years old, she said.
Papsa is designed to be culturally universal. One way of thinking about this is to imagine that it is designed to be relatively natural for not just humans across time and space, but also for non-human animals, artificial intelligences, and aliens. The phonology tries not to lean too heavily on distinctions that are only present in some languages (e.g. tones, voicedness, aspiration), and while the romanized text is shaped for Anglophones, the base orthography is intended to be accessible to everyone.
As a result of this universality, Papsa avoids making presumptions about the people who
use it. Gender is naturally absent, for example, and words like `nup` apply just as much to
non-human people as they do to humans. (The definition includes lots of human-centric words
because that's often a reasonable way to translate the word into English, which is
human-centric.) The core words don't have any of the "human universal" small words like
"dog" and "nose" because, again, not all beings will find those things to be basic concepts.
It's normal for Papsa to get adapted into specific contexts through importing important
concepts via naming them. For example, English speakers might recognize the Papsa name
`iuman` as the word for "human", even though non-English Papsa speakers would not. Still,
there are some proper nouns that are so important and common that they get universal names.
| Papsa Name | English | Etymology | Notes | |-------------|-------------------|-----------------------|-------| | `vufua` | Earth | `vufuh` | | | `vufuduta` | Luna | `vufudut` | | | `guixuvina` | Sol | `guixuh` + `vin` | Often just "ina" | | `vufusauma` | Milky Way | `vufusaum` | | | `mazgu` | day | `mazguih` | 24 hour period | | `aihat` | sit | `fifsaihat` (& `hait`) | Papsa is intended to be so neutral that it doesn't even privilege the human form. As such, there's no core word for sitting, as that pose isn't universal among body plans. The closest core word is `fifsaihat` (literally "half pose"), which is used for deliberately being diagonal. `aihat` is a pun on that and `hait`, since sitting down can also make someone take up less space. | | `ikneimfic` | laugh | `hik` + `neimfic` | Where `neimfic` is about the experience of humor, `ikneimfic` is about the sound of laughter and the specific expression of amusement. Can also be used to capture humorless laughter. | | `ikneimcun` | cry, weep | `hik` + `neimcun` | Where `neimcun` is about the experience of sadness, `ikneimcun` is about the particular act of crying and weeping that humans do, regardless of the feeling behind it (e.g. happy crying still counts). | | `nupukpi` | man, men | `nupmuksukpif` | Shorthand for an adult human who is `nupmuksukpif`, for those who want gendered language. | | `nupukfu` | woman, women | `nupmuksukfun` | Shorthand for an adult human who is `nupmuksukfun`, for those who want gendered language. | | `ifuh` | East | `hifuh` | Up, on a Papsa map. | | `ifsuk` | West | `hifsuk` | Down, on a Papsa map. | | `ifpun` | South | `hifpun` | Right, on a Papsa map. | | `iftaz` | North | `hiftaz` | Left, on a Papsa map. | | `aisbib` | fingertip unit | `nais` + `bibih` | | | `aisas` | stretch unit | `nais` + `has` | | | `aisasuh` | tower unit | `nais` + `hasuh` | | | `aisin` | Papsan Mile | `nais` + i + n | | | `aiskufut` | ride unit | `nais` + `kufut` | | | `aisuif` | border unit | `nais` + `nuif` | | | `aisasat` | thread unit | `nais` + `hasait` | | | `aisasatfic` | fiber unit | `aisasat` + `fic` | | | `aisiguk` | red unit | `nais` + `figuk` | | | `aistikmuk` | biomachine unit | `nais` + `tikmuk` | | | `aisficat` | molecule unit | `nais` + `ficait` | | | `aisikiaz` | x-ray unit | `nais` + `ikiaz` | | | `uizinarc` | blink unit | "uiz-" + `cinarc` | | | `uizbikuh` | breath unit | "uiz-" + `bikuh` | | | `uizimcuk` | rest unit | "uiz-" + `kimcuk` | | | `uizeimtif` | visit unit | "uiz-" + `neimtif` | | | `uaskuh` | go-speed unit | `kuh` | | | `ikaih` | radio/infrared | `fik` + "-aih-" | | | `iksoun` | red | `fik` + `soun` | | | `iksaum` | yellow | `fik` + `saum` | | | `iksuk` | green | `fik` + `suk` | | | `iksat` | cyan | `fik` + `sat` | | | `ikseip` | blue | `fik` + `seip` | | | `iksif` | purple/magenta | `fik` + `sif` | | | `ikiaz` | x-ray/ultraviolet | `fik` + "-iaz-" | | | `suafou` | ℵ₀ | `suaf` + `soun` | | | `iasuafou` | 𝔠, the continuum | "-iaz-" + `suafou` | 6^ℵ₀ = 2^ℵ₀ = 𝔠 | | `ninaisibazi` | Category Theory | `ninais` + `sibaz` | | | `eijuiskat` | ln, the natural logarithm | `ei` + `jamz` + `ui` + `skat` | `kan ei jamz ui skat` |
for (const [name,row] of papsaNames["Papsa Name"]) {
names[baseLetters(name)] = { word: name, meaning: row['English'], notes: row['Notes'] };
}
For practical purposes,
the basic units of measurement in Papsa are, like the SI units, based on the cesium standard.
Specifically, one full oscillation of cesium-standard light is exactly 3.26122557 centimeters, or approximately 1.28 inches.
In Papsa, this unit goes by the name `aisbib`, which translates to something like "standard galactic fingertip".
All standard Papsa units of distance start with the "ais-" prefix and end with a consonant so that they can be followed
by `ei` and then the quantity, just like any noun. The other units of length are multiples of 36 `aisbib`.
aisbib ei sat | 3 Fingertips (≈ 9.78cm or 3.85 inches) aisbib ei slounaum faftaz aisas ei saum | 36 Fingertips = 1 Stretch (≈ 117.4 cm ≈ 46 inches) aisas ei slounaum faftaz aisasuh ei saum | 36 Stretches = 1 Tower (≈ 42.3 m ≈ 140 feet) aisasuh ei slounaum faftaz aisin ei saum | 36 Towers = 1 Mile (≈ 1.52 km ≈ 0.95 (imperial) miles) aisin ei slounaum faftaz aiskufut ei saum | 36 Miles = 1 Ride (≈ 55 km ≈ 34 miles) aiskufut ei slounaum faftaz aisuif ei saum | 36 Rides = 1 Border (≈ 1972 km ≈ 1225 miles)
And of course, just like we can go up in size, we can go down.
The unit `aisiguk` is the equivalent of about 700nm, which is at the outer edge of
human color vision, making the etymology of its name — Red — particularly fun. We get
a similar name with the smallest named distance unit, `aisikiaz` — X-ray.
aisbib ei slaihaum faftaz aisasat ei saum | 1/36 Fingertip = 1 Thread (≈ 0.91 mm) aisasat ei slaihaum faftaz aisasatfic ei saum | 1/36 Thread = 1 Fiber (≈ 25 μm) aisasatfic ei slaihaum faftaz aisiguk ei saum | 1/36 Fiber = 1 Red (≈ 699 nm) aisiguk ei slaihaum faftaz aistikmuk ei saum | 1/36 Red = 1 Biomachine (≈ 19.4 nm) aistikmuk ei slaihaum faftaz aisficat ei saum | 1/36 Biomachine = 1 Molecule (≈ 0.54 nm) aisficat ei slaihaum faftaz aisikiaz ei saum | 1/36 Molecule = 1 X-ray (≈ 0.015 nm)
To talk about a region or volume, mark the exponent via a numerical syllable after "ais-" and before the rest of the unit.
aisukbib | square-Fingertip (≈ 10.6 cm²) aisatbib | cubic-Fingertip (≈ 34.7 cm³) aisukuif | square-Border (≈ 40% of the area of the USA) aisatuif | cubic-Border (≈ 1/3 the volume of Luna)
Units of time are related to space via the speed of light, which in Papsa is called `kuzuip`
— full-speed. Where space uses "ais-", time uses "uiz-". If the other prefix is used for either
kind of unit, a light-speed conversion is applied. The units of duration pick up where the units
of distance become inhuman.
uizuif ei sat | 3 Light-Borders (≈ 19.7 milliseconds) uizuif ei slounaum faftaz uizinarc ei saum | 36 Light-Borders = 1 Blink (≈ 0.24 seconds) uizinarc ei slounaum faftaz uizbikuh ei saum | 36 Blinks = 1 Breath (≈ 8.5 seconds) uizbikuh ei slounaum faftaz uizimcuk ei saum | 36 Breaths = 1 Rest (≈ 5 minutes) uizimcuk ei slounaum faftaz uizeimtif ei saum | 36 Rests = 1 Visit (≈ 3 hours) aiseimtif ei saum | 1 Light-Visit (≈ 22 times the distance from the earth to the sun)
And of course, one can talk about days and years and such using special names. Consult the dictionary for your unit of choice.
To talk about units like speed, one can either use names for the specific units, or combine units by multiplying/dividing them just as though they were numbers. By appending "-aih-" to the prefix, one naturally gets the reciprocal unit.
aisin ei uizaiheimtif ei sat | 3 Miles per Visit (≈ 1.5 kph ≈ 0.95 mph) aisas ei uizaihbikuh ei suk | 2 Stretches per Breath (≈ 1 kph) aisas ei uizaihbikuh ei saum faftaz uaskuh ei saum | 1 Stretch-per-Breath = 1 Go-Speed uaskuh ei siaz naiskuh li iuman kuh | 6 Go-Speeds is how fast humans go (at a slow stroll) iuman sukiaz uaskuh | Humans measure 12 in terms of go-speed (at a brisk walk)
TODO MASS, CHARGE, ETC.
For more on units of measure, see various essays on Utopian Dreams.
Papsa takes a three-fold approach to color. First, we can talk in terms of wavelengths. Light with a
wavelength of `aisiguk ei saum` — "1 Red-length" — for example, is on the far red side of
the spectrum, while light that has a wavelength of half that is just barely ultraviolet. By augmenting
`fik`, the word for color, with a wavelength, we can be very precise. When only a fractional
number is given, the units are assumed to be Red-lengths.
fik ei um aisiguk slaiheikt | 24/36 Red color (≈ 466 nm; #0097FF; "dodger blue") fik ei slaiheikt | (Same as above) fik ei slaihaub | 29/36 Red color (≈ 563 nm; #CCFF00; "electric lime")
But that's clunky. Most Papsa speakers use a simpler color system that has six colors, made by
compounding `fik` with other root words. These six colors can also be referenced by digits
that ascend from red through the color wheel. Because this numerical mapping is totally human-centric,
these words are names, rather than compounds, and drop the opening `f`. The fifth color, magenta, exists
only in the mind, and doesn't correspond to a single wavelength of light.
| Digit | Element | English | Elemental Compound Word | Name | |-------|---------|---------|-------------------------|-----------| | 0 | Fire | Red | `figuk` | `iksoun` | | 1 | Light | Yellow | `figuih` | `iksaum` | | 2 | Plants | Green | `fikbup` | `iksuk` | | 3 | Sky | Cyan | `fikuh` | `iksat` | | 4 | Water | Blue | `fikmum` | `ikseip` | | 5 | Mind | Magenta | `fiknuŋ` | `iksif` |
Papsa addresses are usually given as positive-number coordinates in some local region. Coordinates, whether on a map or not, are usually read as the number of units to the right, followed by the number of units down (then followed by the number of units forward, if in 3D). Since Papsa maps are typically oriented with East at the top, the first coordinate is how far south, and then the second is how far West of the reference point. Units for local regions are assumed to be six-stretches long (about 7 meters), so a Papsan Mile (≈ 1.52 km ≈ 0.95 (imperial) miles) is exactly 216 units.
While plain numbers can be used, addresses in Papsan culture are usually encoded as words for
ease of remembering and more efficient communication. These address words, each have
three primal consonants, which can be seen as (base-six) digits of a number from 0 to 215. Like all Papsa
numbers, they're little-endian, so `tigab` (t-g-b, 3-2-4) is 3 + 2×6 + 4×36 = 159.
Using two such words for each dimension, one can cover a province about 329 km on a side. The name of the
reference region is given between the parts of the address.
tigab figuk olguin nikud pauneim | "Red Net", Olguin, "Crying Motive" (ie 19383 units south and 8760 west of the Olguin reference point)
TODO: A lot of the necessary words don't exist, so I've filled them with names that follow the pattern _a_a_a for now. This is a stopgap to keep from needing to add a ton of random, forced vocabulary at this stage in the language's development. The final state of the table should have entirely defined names and compounds.
| Base | +0 | +1 | +2 | +3 | +4 | +5 | |------|-----------|----------|----------|----------|-----------|-----------| | 0 | `naunuŋ` | `miniŋ` | `kanuŋ` | `tinuŋ` | `paunaun` | `funuŋ` | | 6 | `neimin` | `mamin` | `kimain` | `tumain` | `pimain` | `feimain` | | 12 | `nikan` | `mukan` | `kukan` | `tikan` | `bikan` | `fikan` | | 18 | `natana` | `matana` | `katana` | `tautun` | `patana` | `fatana` | | 24 | `nupun` | `mapana` | `kapana` | `dupaun` | `papana` | `fapana` | | 30 | `nafana` | `mafana` | `kuvin` | `tafana` | `pafana` | `fafana` | | 36 | `nuneim` | `manama` | `kuneim` | `tanama` | `pauneim` | `fanama` | | 42 | `neimam` | `mumum` | `kimam` | `tamama` | `pimum` | `feimum` | | 48 | `nakama` | `makama` | `kakama` | `takama` | `pakama` | `fakama` | | 54 | `natama` | `matama` | `katama` | `tatama` | `patama` | `fatama` | | 60 | `napama` | `mapama` | `kapama` | `tapama` | `papama` | `fapama` | | 66 | `nuifeim` | `mafama` | `kafama` | `tafama` | `pafama` | `fafama` | | 72 | `nanaka` | `manaka` | `kanaka` | `tanaka` | `punik` | `fanaka` | | 78 | `neimik` | `mumuk` | `kamaka` | `tamaka` | `pamaka` | `famaka` | | 84 | `nakaka` | `makaka` | `kakaka` | `takaka` | `pakaka` | `figuk` | | 90 | `natik` | `mataka` | `kataka` | `tataka` | `pataka` | `futik` | | 96 | `napaka` | `mapaka` | `kapaka` | `tapaka` | `papaka` | `fapaka` | | 102 | `nafaka` | `mafaka` | `kafaka` | `tafaka` | `pafaka` | `fafaka` | | 108 | `nanata` | `manata` | `kanata` | `tanata` | `panata` | `vanata` | | 114 | `namata` | `mamata` | `kamata` | `tamata` | `pamata` | `famata` | | 120 | `nikud` | `mukait` | `gukait` | `takata` | `pakata` | `fakata` | | 126 | `natata` | `mutaut` | `katata` | `taudut` | `patata` | `fatata` | | 132 | `napata` | `mapata` | `kapata` | `tapata` | `papata` | `fapata` | | 138 | `nafata` | `mafata` | `kufut` | `tifait` | `pafata` | `fafait` | | 144 | `nanapa` | `mainup` | `kanapa` | `tanapa` | `paunup` | `fanapa` | | 150 | `namapa` | `mamapa` | `kamapa` | `tamapa` | `pamapa` | `famapa` | | 156 | `nakapa` | `makapa` | `kakapa` | `tigab` | `pakapa` | `figab` | | 162 | `natapa` | `matapa` | `katapa` | `tatapa` | `patapa` | `fatapa` | | 168 | `napapa` | `mapapa` | `kapapa` | `dapapa` | `papapa` | `vapapa` | | 174 | `nafapa` | `mafapa` | `kavapa` | `tafapa` | `pafapa` | `vafapa` | | 180 | `ninuif` | `minuif` | `kanafa` | `tanafa` | `panafa` | `vanafa` | | 186 | `neimuf` | `mamafa` | `kimif` | `tamafa` | `pamafa` | `feimuf` | | 192 | `nakafa` | `maukuf` | `gakafa` | `takafa` | `pakafa` | `fikuf` | | 198 | `natafa` | `matafa` | `katafa` | `tautuf` | `patafa` | `fatafa` | | 204 | `napafa` | `mapafa` | `gapif` | `dupuf` | `papuf` | `faipif` | | 210 | `nuivuf` | `mufif` | `kuvuf` | `tivuf` | `pifif` | `favuf` |
// Every address word spells its own number: its consonants are the number's base-six digits, little-endian.
for (const [base, row] of addressWords['Base']) for (let d = 0; d < 6; d++) {
const word = row['+' + d];
const digits = [...baseLetters(word)].filter(l => !vowelChars.has(l)).map(primalNum);
const value = digits.reduce((sum, x, i) => sum + x * 6 ** i, 0);
if (digits.length !== 3 || value !== +base + d) throw new Error(`${word} spells ${value}, not ${+base + d}`);
}
// Each word is glossed by its first noun sense in the dictionary, or its meaning if it's a name.
// Placeholder names that aren't defined yet are allowed, and are gathered into a single warning.
// Because compounds are defined further down the page, we wait to add glosses until after the page loads.
addEventListener('DOMContentLoaded', () => {
const undefinedNames = [];
for (const code of document.querySelectorAll('#address-words td code')) {
const word = code.textContent, key = baseLetters(word);
const core = coreWords[key], name = names[key];
let sense;
if (core) sense = [core.nounMeaning, core.intransitiveMeaning?.replace(/^to /, '')].find(s => s && s !== '-');
else if (name) sense = name.meaning;
else if (['name', 'pronoun'].includes(wordType(wordIdxs(word)))) { undefinedNames.push(word); continue; }
else { reportSpecError('Address Words', `${word} isn't in the dictionary`); continue; }
if (sense) code.after(` ${firstFew(sense, 1)}`);
}
const n = undefinedNames.length;
if (n) reportSpecWarning('Address Words', `${n} placeholder name${n === 1 ? " isn't" : "s aren't"} defined yet: ${undefinedNames.join(', ')}`);
});
Papsa associates the axes of a coordinate system with numbers, starting with time. Positive means later, right, down, and forward; negative means earlier, left, up, and back. In addition to the numeric words, axes are often referenced by phrases. Directions also have non-numerical words, which are sometimes favored, and sometimes disfavored, depending on the word. Disfavored terms, in the following table, are provided in parentheses.
| Axis | Word | Phrase | Positive Direction | Negative Direction | |------|-----------|---------------|-----------------------------------|---------------------------------| | 0 | `fifsoun` | `fif ei zus` | later: `zeif` (`hifsoun`, `zouc`) | earlier: `zous` (`hifcroun`) | | 1 | `fifsaum` | `fif ei taz` | right: `hifpun` (`hifsaum`) | left: `hiftaz` (`hifcraum`) | | 2 | `fifsuk` | `fif ei xuh` | down: `hifsuk` (`hifceim`) | up: `hifuh` (`hifcruk`) | | 3 | `fifsat` | `fif ei tuah` | forward: `hifsat` (`hiftiŋ`) | back: `hiftuah` (`hifcrat`) |
Since Papsa maps put East at the top, the compass directions are names taken from the common
words for the directions on a map, dropping the h: `ifuh` (East),
`ifpun` (South), `ifsuk` (West), and `iftaz` (North).
Coordinate variables are pronouns, as in algebra, but usually not the generic `isnu`,
`isma`, and `iski`. The horizontal variable is `ifpu`, or
`ifzei` when the horizontal axis stands for time. The vertical is `ifsu`
(still with positive meaning down), and the forward one is `ifti`.
The broadest word for living things is `muk`, and it's the right word when
speculating about life on other planets. Three core words are more specific.
`mauk` is for animals, or more generally, organisms with behavior, moving
about and processing information in complex ways. `bup` is for plants, but also
for fungi, seaweed, and anything else that's big and stationary. `coum` is for
germs and bugs: bacteria, viruses, and other microorganisms, along with invertebrates and
parasites more generally. (Prions count as `coum`, even though they're probably not
`muk`.) Humans are understood to be `mauk`, but the usual word for them is
`nup`.
Like Aristotle and young children, Papsa sorts organisms by their most salient traits rather
than by their ancestry. Compounds like `buptait` (tree) carve reality at its obvious
joints. (Important, since there's no such thing as
a tree from a phylogenetic perspective!) Papsa can talk about phylogeny too, but it's rarer than
naming things by how they look. In natural edge cases, like jellyfish, barnacles, or Venus flytraps,
speakers must simply make a judgment call for that context, or pick a word specific enough to fit
cleanly.
nup mauk | People are animals. buptait bup | Trees are plants. coum muk sou | Are germs alive? mauk ei bup | It's a plant-like animal (like a barnacle). coumas maukcoum | Caterpillars are bugs. knidaria maukmum | Cnidarians are sea-creatures.
Papsa compounds are too long, too vague, or both, for most particular kinds of animal. Many
regions have local names, easy for bilingual children, like `kiti` or
`dagi`. But the international way to be specific is to import the Latin name from
biological taxonomy, the way Italian and Spanish inherited their nouns: take the accusative form,
drop the final -m, and spell what's left with its classical pronunciation. Short
e and o are open enough to become `a`, while long ē and
ō close up into `i` and `u`. The diphthongs ae and
au become "ai" and "au", consonantal v and i are the vowels
`u` and `i`, y becomes `i`, and
c is always hard. This matches classical Latin pronunciation, and so diverges sometimes
from how English speakers are used to reading Latin names. (For example, pronouncing the leading
plosive in `knidaria`.) So Canis (accusative canem) becomes `kana`,
Fēlis (fēlem) becomes `fila`, and Lepidoptera becomes
`lapidaptara`. The Greek digraphs th, ph, ch, and rh were aspirated in classical Latin,
and since Papsa doesn’t distinguish aspiration, they simply lose their h (so Philodendron becomes
pilodandru).
In some cases, the Latin word is too small to produce a Papsa name. Consider sheep, whose Latin name is Ovis (accusative ovem) and would get written in Papsa as "aua". In these cases, one can prepend the name with "mauk-", or — more typically — just use a nearby word that means roughly the same thing (e.g. Ovis aries or Caprinae).
| Papsa Name | English | Latin | Notes | |---------------|----------------------|-----------------------|-------| | `kana` | dog | Canis | Strictly the genus, which also includes wolves, coyotes and jackals. | | `fila` | cat | Felis | Also small wildcats. | | `mura` | mouse | Mus | | | `ratu` | rat | Rattus | | | `galu` | chicken | Gallus | | | `kapra` | goat | Capra | | | `rana` | frog | Rana | | | `farmika` | ant | Formica | | | `lapidaptara` | butterfly, moth | Lepidoptera | An order, rather than a genus. |
for (const [name,row] of organismNames["Papsa Name"]) {
names[baseLetters(name)] = { word: name, meaning: row['English'], notes: row['Notes'] };
}
Papsa is designed to be especially good for mathematics, so it has more here than one might expect. This appendix covers:
Numbers · Fractions · Scientific Notation · Negative Numbers · Constants · Algebra · Functions · Set, Type, and Category Theory · Tensors and Linear Algebra · Infinity and Infinitesimal Calculus · Proofs and Logic
Papsa leans heavily on base six, the best radix. This shows up in the way the primal consonants in grammar words can indicate quantity, but it's also prominent in the counting words. When children are first learning to count, they use the basic numbers, which range from zero to five (and can thus be represented by fingers on one hand). These numbers all start with "s" and have distinct vowels, roughly in alphabetical order (with primes given single vowels, and thus diphthongs being treated as preceding singles), as well as the corresponding primal consonant at the end.
| Papsa Word | Arabic Numeral | Papsa Numeral | |------------|----------------|---------------| | `soun` | 0 | `⓪` | | `saum` | 1 | `①` | | `suk` | 2 | `②` | | `sat` | 3 | `③` | | `seip` | 4 | `④` | | `sif` | 5 | `⑤` |
These root words can be combined in regular ways to express any natural number, using little-endian base-six.
If we combine `sat` with `sat`, for example, we drop the middle `s` and
just get `satat` — 33 in base-six, which is 3×1 + 3×6 = twenty-one. Again, this is
little endian, so we read the digits as first the number of
ones, then the number of sixes, then the number of thirty-sixes, and so on. In this way, the reader can (theoretically)
always tell what digit they're on, even when the number is long.
sukif | 2×6⁰ + 5×6¹ = thirty-two sifouneipaum | 5×6⁰ + 0×6¹ + 4×6² + 1×6³ = three-hundred-sixty-five sounatukeipififat | 0×6⁰ + 3×6¹ + 2×6² + 4×6³ + 5×6⁴ + 5×6⁵ + 3×6⁶ = one-hundred-eighty-six-thousand, two-hundred-eighty-two
This number system is more compact than spoken English, but because six is less than ten, it's slightly less efficient when writing out digits. But adults do not usually use the basic numbers when writing. Instead, they use compact numbers. Compact numbers are still thought of as being "in base six" for most purposes, such as arithmetic. But they're "stored with two digits per syllable/symbol," in effect using base thirty-six. In order not to clutter the namespace, compact numbers all start with "sl". Like basic numbers, they can be combined in a little-endian fashion, dropping the "sl" when it occurs in the middle of a word.
| Papsa Word | Base ten | Rule | |------------|----------|-------------| | `sloun` | 0 | basic digit | | `slaum` | 1 | basic digit | | `sluk` | 2 | basic digit | | `slat` | 3 | basic digit | | `sleip` | 4 | basic digit | | `slif` | 5 | basic digit | | `slaukt` | 6 | 1 × 6 | | `slaux` | 7 | prime | | `slurt` | 8 | 2³ | | `slark` | 9 | 3² | | `sluf` | 10 | 2 × 5 | | `slauj` | 11 | prime | | `slukt` | 12 | 2 × 6 | | `slauz` | 13 | prime | | `slux` | 14 | 2 × 7 | | `slaf` | 15 | 3 × 5 | | `slurp` | 16 | 2⁴ | | `slaur` | 17 | prime | | `slakt` | 18 | 3 × 6 | | `slaug` | 19 | prime | | `sleif` | 20 | 4 × 5 | | `slax` | 21 | 3 × 7 | | `sluj` | 22 | 2 × 11 | | `slaud` | 23 | prime | | `sleikt` | 24 | 4 × 6 | | `slirk` | 25 | 5² | | `sluz` | 26 | 2 × 13 | | `slart` | 27 | 3³ | | `sleix` | 28 | 4 × 7 | | `slaub` | 29 | prime | | `slikt` | 30 | 5 × 6 | | `slauv` | 31 | prime | | `slurf` | 32 | 2⁵ | | `slaj` | 33 | 3 × 11 | | `slur` | 34 | 2 × 17 | | `slix` | 35 | 5 × 7 |
Larger compact numbers string these digits together:
slounaum | Thirty-six slaumaum | Thirty-seven slukaum | Thirty-eight slifuf | Three-hundred-sixty-five slaktuzixat | One-hundred-eighty-six-thousand, two-hundred-eighty-two
Astute readers will notice that there are patterns in the names of the compact numbers that extend beyond what we've already discussed. In particular:
Numbers occupy a somewhat unique position, linguistically, in that they are right on the border
between regular patterns in reality — implying they should have core words — and unique entities that
can be given names. As such, in addition to the core words described here, it's also totally valid to
prefix a number with "ou" or to drop the leading "sl" if it's clear from context that you're naming
a number. So, for example, `slifuf`, `ouslifuf`, and `ifuf`
all refer to three-hundred-sixty-five. The first form is the most normal, the second form is useful to
clarify that you're talking about the number itself, rather than a quantity of real things (like days),
and the final form is useful if you want to be even more terse.
const digitSuff = []; // After being populated below, holds 36 strings for the digit suffixes.
const digitVowel = []; // Same, but only for the vowels.
const bySuffix = {}; // Base-letter suffix -> corresponding numerical digit
const registerDigit = (n,s) => { bySuffix[baseLetters(s)] = n; digitSuff[n] = s; digitVowel[n] = s.replace(/[^aeiou]+$/i,''); };
for (const [word, row] of basicNumbers['Papsa Word']) registerDigit(+row['Arabic Numeral'], word.slice(1)); // 0-5
for (const n of [6,12,18,24,30]) registerDigit(n, digitVowel[n/6]+'kt');
const primeConsonants = {7:'x',11:'j',13:'z',17:'r',19:'g',23:'d',29:'b',31:'v'}; // Not counting 2,3,5
for (const n in primeConsonants)
for (let a = 1; a*n < 36; a++)
registerDigit(a*n, digitVowel[a]+primeConsonants[n]);
for (const n of [3,4,5]) registerDigit(2 ** n, digitVowel[2]+'r'+digitSuff[n].slice(-1));
for (const n of [2,3]) registerDigit(3 ** n, digitVowel[3]+'r'+digitSuff[n].slice(-1));
registerDigit(5 ** 2, digitVowel[5]+'r'+digitSuff[2].slice(-1));
for (const a of [2,3,4]) registerDigit(a*5, digitVowel[a]+digitSuff[5].slice(-1));
// Consumes a string of BASE LETTERS and returns the corresponding digits
const lettersToDigits = (s, compact) => {
const xs = s.match(/[UAI]*[^UAI]+|[UAI]+/g) ?? [];
return xs.map(x => compact || bySuffix[x] < 6 ? bySuffix[x] : undefined);
};
const digitsToNum = (ds, radix) => ds.reduceRight((n, d) => n * radix + d, 0);
const numberDigits = (n, radix) => {
const ds = []; do { ds.push(n % radix); n = Math.floor(n / radix); } while (n > 0);
return ds; // little-endian
};
const numberWord = (n, compact) =>
(n < 0 ? 'cr' : compact ? 'sl' : 's') + numberDigits(Math.abs(n), compact ? 36 : 6).map(d => digitSuff[d]).join('');
// Base letters of a number word -> whether it's compact, its sign, and its syllable groups after the prefix.
// Negative numbers are always compact; leaving out the prefix means a positive compact number.
const numberParts = (base) => {
const prefix = base.match(/^(?:UU)?(?:SL?|CL)/)?.[0] ?? "";
return { compact: ! prefix || prefix.endsWith("L"), sign: prefix.includes("C") ? -1 : 1,
groups: base.slice(prefix.length).match(/[UAI]*[^UAI]+|[UAI]+/g) ?? [] };
};
// The digit rules above must give each number from 0 to 35 a syllable of its own.
for (let n = 0; n < 36; n++)
if (bySuffix[baseLetters(digitSuff[n] ?? '')] !== n)
fail(`Compact digit ${n} (${digitSuff[n]}) is missing or shares its syllable with another digit.`);
// The compact-number table must list 0 to 35 in order, each spelled as the rules above spell it.
for (const [word, row] of compactNumbers['Papsa Word']) {
const n = +row['Base ten'], rule = row['Rule'];
if (row.index !== n) fail(`The compact-number table should list ${row.index} where it lists ${n}.`);
if (numberWord(n, true) !== word) fail(`${word} should be ${numberWord(n, true)}, the compact word for ${n}.`);
const [, a, b] = rule.match(/^(\d+) × (\d+)$/) ?? [];
const [, base, exp] = rule.match(/^(\d+)([²³⁴⁵])$/) ?? [];
const holds = rule === 'basic digit' ? n < 6
: rule === 'prime' ? n in primeConsonants
: a ? a * b === n
: base ? base ** ('²³⁴⁵'.indexOf(exp) + 2) === n
: false;
if ( ! holds) fail(`The rule "${rule}" doesn't give ${n}.`);
}
// Intransitive, a number counts its subject. Transitive, it measures the subject in the object's dimension.
const numberMeanings = (n) => ({
intransitiveMeaning: `to number ${n}, to exist ${n} times, to have ${n} when counting`,
transitiveMeaning: `to measure ${n} units in`,
nounMeaning: `${n}`,
adjectiveMeaning: `${n}`,
adverbMeaning: `${n} times`,
});
const numberEntry = (n, compact) => ({
word: numberWord(n, compact),
value: n,
baseSix: (n < 0 ? '-' : '') + numberDigits(Math.abs(n), 6).join(''),
...numberMeanings(n),
});
// Papsa numbers written with letters.
// Splice instead of push to go after the grammar tokenizer but before the generic name tokenizer.
tokenParsers.splice(1,0,(s,base,ixs) => {
const { compact, sign, groups } = numberParts(base);
const digits = lettersToDigits(groups.join(''), compact);
const n = digitsToNum(digits, compact ? 36 : 6);
if ( ! digits.length) return; // A bare prefix, like the letter `S`, isn't a number.
if ( ! Number.isFinite(n)) return; // Don't parse NaN
return { rawTok: s, type: 'number', ...numberEntry(sign * n || 0, compact) };
});
Numbers that aren't whole are written by placing the radix syllable "-aih-" between the whole part and the fractional part, much like a decimal point in English. It works the same way for basic and compact numbers. If the whole part is zero, it can be left out entirely.
Before the radix syllable, digits are little-endian, as described above. For basic numbers we first see the ones, then the sixes, then the thirty-sixes, and so on. In the fractional part, digits run the other way: first the sixths, then the thirty-sixths, and so on. In this way the rule that the listener always knows which place the current syllable occupies is preserved.
saihaum | one sixth — 0.1 in base six ≈ 0.1667 slaihaum | one thirty-sixth — 0.01 in base six ≈ 0.0278 slaihaukt | one sixth — 0.6 in base thirty-six saumaihat | one and a half — 1.3 in base six slaumaihakt | one and a half — 1.I in base thirty-six slataihifatakt | 3 + 5/36 + 3/36² + 18/36³ ≈ 3.14159 (π, approximately) pun bik bik ei saihat | I ate half the food.
Base six handles the fractions of everyday life well.
| Fraction | Base six | Basic | Compact | |----------|----------|-----------------------|-------------------| | 1/2 | 0.3 | `saihat` | `slaihakt` | | 1/3 | 0.2 | `saihuk` | `slaihukt` | | 2/3 | 0.4 | `saiheip` | `slaiheikt` | | 1/4 | 0.13 | `saihaumat` | `slaihark` | | 3/4 | 0.43 | `saiheipat` | `slaihart` | | 1/6 | 0.1 | `saihaum` | `slaihaukt` | | 1/8 | 0.043 | `saihouneipat` | `slaiheipakt` | | 1/9 | 0.04 | `saihouneip` | `slaiheip` | | 1/12 | 0.03 | `saihounat` | `slaihat` | | 1/36 | 0.01 | `saihounaum` | `slaihaum` | | 1/5 | 0.1111… | `saihaumaumaumaum`… | `slaihauxaux`… | | 1/10 | 0.0333… | `saihounatatat`… | `slaihatax`… |
// Fractional digits in either radix -> big-endian base-six digits, minus trailing zeros.
const fracBaseSix = (ds, compact) => {
const out = compact ? ds.flatMap(d => [Math.floor(d / 6), d % 6]) : [...ds];
while (out.length > 1 && out.at(-1) === 0) out.pop();
return out;
};
const radixMark = baseLetters('aih');
// Digit groups with at most one radix mark -> { value, baseSix }, or undefined if malformed.
const groupsToQuantity = (groups, compact) => {
const radix = compact ? 36 : 6;
const at = groups.indexOf(radixMark);
if (groups.lastIndexOf(radixMark) !== at) return; // At most one radix mark.
const whole = lettersToDigits((at === -1 ? groups : groups.slice(0, at)).join(''), compact); // Empty means an elided zero.
const frac = at === -1 ? [] : lettersToDigits(groups.slice(at + 1).join(''), compact);
if ( ! (at === -1 ? whole : frac).length) return;
const wholeN = digitsToNum(whole, radix);
const value = wholeN + frac.reduceRight((f, d) => (f + d) / radix, 0);
if ( ! Number.isFinite(value)) return; // Unknown digit, or a compact digit in a basic number.
return { value, baseSix: numberDigits(wholeN, 6).join('') + (frac.length ? '.' + fracBaseSix(frac, compact).join('') : '') };
};
const quantityEntry = (s, value, baseSix) => {
if (typeof value === 'number') value = +value.toPrecision(10); // Imaginary values are strings, like "3i".
return { rawTok: s, type: 'number', word: s, value, baseSix, ...numberMeanings(value) };
};
// The other way: a value -> its digits in a radix. That's the whole part (little-endian) and up to `places`
// fractional digits, cut short (like the "…" rows above) unless they end. Values are only precise to about ten
// digits, and each fractional digit multiplies that error by the radix, so a digit within the error rounds up,
// and `places` should stay where the error is still small.
const quantityDigits = (value, radix, places) => {
const v = Math.abs(value);
let err = 1e-10 * Math.max(1, v), whole = Math.floor(v + err), frac = Math.max(0, v - whole);
const ds = [];
while (ds.length < places && frac > err) {
frac *= radix; err *= radix;
const d = Math.min(radix - 1, Math.floor(frac + err));
ds.push(d); frac = Math.max(0, frac - d);
}
return { whole: numberDigits(whole, radix), frac: ds, exact: frac <= err };
};
// A value -> its basic or compact number word (there are no negative basic numbers), and whether it's exact.
const quantityWord = (value, compact) => {
if (value < 0 && ! compact) return;
const { whole, frac, exact } = quantityDigits(value, compact ? 36 : 6, compact ? 5 : 10);
const spell = (ds) => ds.map(d => digitSuff[d]).join('');
const wholePart = frac.length && whole.join('') === '0' ? '' : spell(whole); // A zero whole part is left out.
return { word: (value < 0 ? 'cr' : compact ? 'sl' : 's') + wholePart + (frac.length ? 'aih' + spell(frac) : ''), exact };
};
// Each common fraction must be spelled as its value is. A "…" row repeats forever, so it's cut short.
for (const [fraction, row] of commonFractions['Fraction']) {
const [num, den] = fraction.split('/').map(Number);
for (const col of ['Basic', 'Compact']) {
const listed = row[col].replace('…', ''), { word, exact } = quantityWord(num / den, col === 'Compact');
if (row[col].endsWith('…') ? exact || ! word.startsWith(listed) : word !== listed)
fail(`The ${col.toLowerCase()} word for ${fraction} should be ${word}${exact ? '' : '…'}, not ${row[col]}.`);
}
}
// Spliced in ahead of the whole-number parser (which rejects anything containing `aih` anyway).
tokenParsers.splice(1, 0, (s, base) => {
const { compact, sign, groups } = numberParts(base);
if ( ! groups.includes(radixMark)) return; // Whole numbers are left to the whole-number parser.
const q = groupsToQuantity(groups, compact);
return q && quantityEntry(s, sign * q.value, (sign < 0 ? '-' : '') + q.baseSix);
});
// The common-fractions table must agree with the parser. A trailing "…" means the digits repeat,
// so they only need to be right up to the last place shown.
for (const [fraction, row] of commonFractions['Fraction']) {
const [num, den] = fraction.split('/').map(Number);
for (const col of ['Basic', 'Compact']) {
const word = row[col].replace(/…$/, '');
const { compact, groups } = numberParts(baseLetters(word));
const q = groupsToQuantity(groups, compact);
const places = groups.length - groups.indexOf(radixMark) - 1;
const tolerance = row[col].endsWith('…') ? (compact ? 36 : 6) ** -places : 1e-9;
if ( ! q || Math.abs(q.value - num / den) >= tolerance) fail(`${word} isn't ${fraction}.`);
if (q.baseSix !== row['Base six'].replace(/…$/, '')) fail(`${word} is ${q.baseSix} in base six, not ${row['Base six']}.`);
}
}
Just as we can insert a syllable to indicate the transition to the fractional part of a number, we can insert the exponent syllable "-iaz-" to mark that we're multiplying the number that came before by six to the power of whatever number comes after the syllable. If the preceding number is elided, it's assumed to be 1. In other words, this lets us efficiently express things in scientific notation.
The exponent can be elided too, in which case it's also 1. So `siaz` is six.
Since `sounaum` is two syllables and `slaukt` is a mouthful, but six
is a very foundational/special number, `siaz` is the most common representation.
The exponent is a whole number, written with the same kind of digits as the rest of the word: basic digits in a basic number, compact digits in a compact number, and little-endian either way. But the base being raised to that power is always six, even in a compact number.
siaz | 6¹ = six saumiazuk | 1 × 6² = thirty-six siazuk | 6² = thirty-six (the leading 1 is elided) sukiazat | 2 × 6³ = four-hundred-thirty-two siazaumaum | 6⁷ = two-hundred-seventy-nine-thousand, nine-hundred-thirty-six (the exponent is 11 in base six) slataihirkuriazukt | (3 + 25/36 + 34/36²) × 6¹² ≈ 8.1 billion (the population of Earth, approximately) slukaihuzukiazikt | (2 + 26/36 + 2/36²) × 6³⁰ ≈ 6.022 × 10²³ (Avogadro's number, approximately)
To use scientific notation with a negative exponent, use "-iaj-" instead of "-iaz-".
saumiajuk | 1 × 6⁻² = one thirty-sixth siajat | 6⁻³ = one two-hundred-sixteenth sleipaihaugixiajirk | (4 + 19/36 + 35/36²) × 6⁻²⁵ ≈ 1.602 × 10⁻¹⁹ (the charge of an electron in coulombs)
Like the radix syllable "-aih-", the exponent syllables are built on a vowel group that no digit uses — every digit's vowel group is one of "ou," "au," "u," "a," "ei," or "i" — so they can never be mistaken for digits.
const exponentMarks = { [baseLetters('iaz')]: 1, [baseLetters('iaj')]: -1 }; // Mark -> sign of the exponent
// Spliced in ahead of the fraction parser (which rejects anything containing an exponent mark anyway).
tokenParsers.splice(1, 0, (s, base) => {
const { compact, sign, groups } = numberParts(base);
const at = groups.findIndex(g => g in exponentMarks);
if (at === -1 || groups.slice(at + 1).some(g => g in exponentMarks)) return; // Exactly one exponent mark.
const mantissa = at ? groupsToQuantity(groups.slice(0, at), compact) : { value: 1, baseSix: '1' }; // Elided means 1.
const exponent = lettersToDigits(groups.slice(at + 1).join(''), compact);
if ( ! mantissa) return;
const e = exponentMarks[groups[at]] * (exponent.length ? digitsToNum(exponent, compact ? 36 : 6) : 1); // Elided means 1.
if ( ! Number.isFinite(e)) return; // Unknown digit, or a compact digit in a basic number.
return quantityEntry(s, sign * mantissa.value * 6 ** e,
`${sign < 0 ? '-' : ''}${mantissa.baseSix}×6^${e < 0 ? '-' : ''}${numberDigits(Math.abs(e), 6).join('')}`);
});
// Number punctuation must never double as a digit.
for (const mark of [radixMark, ...Object.keys(exponentMarks)])
if (mark in bySuffix) fail(`Number punctuation ${mark} is also the digit ${bySuffix[mark]}.`);
A negative number is a compact number that starts with "cr" instead of "sl". Everything after the prefix works the same way, including the radix and exponent syllables.
craum | −1 crat | −3 crounaum | −36 crifuf | −365 crataihakt | −3.5 (−3.I in base thirty-six) craihaum | −1/36 craumiajat | −1 × 6⁻³ = −1/216 (a negative number with a negative exponent)
There are no negative basic numbers. "c" alone can't be the prefix. Words like
`cuk` are already taken, and children learn advanced numbers well before they need
to go below zero. The "cr" can't be dropped the way "sl" sometimes is, since what's left
would read as a positive number.
Some numbers can't be written exactly with digits. The most important ones get words of their own, starting with "sk". Each takes the digit nearest its value, so it's easy to guess roughly how big it is. (The exception is −1, which takes zero.)
| Papsa Word | Digit | Constant | Value | |------------|-------|-----------------------|--------------| | `skoun` | 0 | −1 | -1 | | `skaum` | 1 | i, the square root of −1 | i | | `skuk` | 2 | φ, the golden ratio | 1.6180339887 | | `skat` | 3 | e, Euler's number | 2.7182818285 | | `skaukt` | 6 | τ, the circle constant | 6.2831853072 |
Digits after a constant multiply it. They're compact digits, and as with any number the radix syllable can mark a fraction. This gives imaginary numbers, and a second way of writing negative numbers.
skaumat | 3i skaumaihakt | 0.5i skounat | −3 (the same as crat)
Papsa uses τ, not π, as the circle constant, and measures angles in turns. A fraction of a circle is τ times that fraction, so the common fractions above double as the common angles.
| Angle | Turn | Papsa | |-------|------|-------------------| | 360° | 1 | `skaukt` | | 180° | 1/2 | `skauktaihakt` | | 90° | 1/4 | `skauktaihark` | | 60° | 1/6 | `skauktaihaukt` | | 45° | 1/8 | `skauktaiheipakt` | | 30° | 1/12 | `skauktaihat` | | 15° | 1/24 | `skauktaihaumakt` | | 10° | 1/36 | `skauktaihaum` |
// Base letters -> { value, baseSix } for a constant times an optional (compact) multiplier, or undefined.
const constantQuantity = (base) => {
const prefix = base.match(/^(?:UU)?SK/)?.[0];
if ( ! prefix) return;
const [c, ...rest] = base.slice(prefix.length).match(/[UAI]*[^UAI]+|[UAI]+/g) ?? [];
const row = constants['Digit'][bySuffix[c]];
if ( ! row) return;
const m = rest.length ? groupsToQuantity(rest, true) : { value: 1, baseSix: '1' }; // Elided means 1.
if ( ! m) return;
if (row['Value'] === 'i') return { value: `${m.value === 1 ? '' : +m.value.toPrecision(10)}i`, baseSix: `${m.baseSix}×i` };
const v = +row['Value'];
return { value: m.value * v, baseSix: v === -1 ? `-${m.baseSix}` : `${m.baseSix}×${row['Constant'][0]}` };
};
tokenParsers.splice(1, 0, (s, base) => {
const q = constantQuantity(base);
return q && quantityEntry(s, q.value, q.baseSix);
});
// Each constant is spelled with its digit.
for (const [word, row] of constants['Papsa Word'])
if (word !== 'sk' + digitSuff[+row['Digit']]) fail(`${word} should be sk${digitSuff[+row['Digit']]}, for digit ${row['Digit']}.`);
// Each angle must be its turn of τ, in degrees.
for (const [word, row] of angles['Papsa']) {
const [num, den = 1] = row['Turn'].split('/').map(Number);
const q = constantQuantity(baseLetters(word));
if ( ! q || Math.abs(q.value - 2 * Math.PI * num / den) > 1e-8) fail(`${word} isn't τ×${row['Turn']}.`);
if (360 * num / den !== parseFloat(row['Angle'])) fail(`${row['Turn']} of a turn isn't ${row['Angle']}.`);
}
// Dictionary names outrank numbers. Without this, a name made only of digit syllables reads as a
// prefixless number (`aihat`, "sit", would be 1/12). Each number parser above was also spliced in at
// index 1, so this one has to come last to land ahead of all of them.
tokenParsers.splice(1, 0, (s, base, ixs) =>
names[base] && { ...names[base], rawTok: s, type: wordType(ixs), probablyForeign: false });
The word `faf` is vague. It can mean "to be," or merely "to relate to."
`faftaz` is specifically about two things having the same value. But in mathematics
the context makes the meaning clear, so equations just use `faf`. To say that two
things aren't equal, invert it by compounding with `juc`.
suk faf suk | 2 = 2 suk fafjuc soun | 2 ≠ 0
`zous` means "precedes," which is vague in a way that context doesn't fix. One
precedes two, but does zero? Mathematics needs an immediate notion, a step of exactly one unit.
It needs an extended notion that isn't just a trivial relation. And it needs a general notion
that allows for equality, because reflexivity gives a more natural structure (as in category
theory's notion of order).
saum zousaum suk | 1 is the immediate predecessor of 2 soun zousis suk | 0 < 2 soun zousfaf suk | 0 ≤ 2
Papsa prefers to put the predecessor in the subject slot and the successor in the object slot.
The relationship can be flipped by using `zouc` instead (`zoucsaum` is
particularly common). But since the two words sound alike, it's more common to flip an inequality
by augmenting it with `jamz`. (`juc` inverts the inequality, sending less-than to
greater-than-or-equal-to and vice-versa.) Most often, we just use the standard direction.
suk zoucfaf suk | 2 ≥ 2 suk zousfaf ei jamz soun | 2 ≥ 0 suk zousis ei jamz soun | 2 > 0
Papsa has three ways to add: grouping phrases, putting several pieces of content in the same
slot, and `sis`. Grouping is the most common, but each has its place. For example,
`sis` is how one explicitly talks about a sum.
saum ou saum faf suk | 1 and 1 are 2 saum nau saum faf suk | 1 and/or 1 are 2 saum sis saum | 1 was added to 1 saum sis saum ul faf suk | 1 added to 1 is 2 sis ui sih faf sleipark | The list's sum is 328.
(Technically, `ou` creates lists/tuples, and lists of numbers are automatically
added together in spots where a single number is expected, such as comparison. See the section on
Type Theory for more details.)
Where `sis` is the word for addition, `suat` is the word for
multiplication. But just as adding is usually done by grouping, multiplying is usually done by
augmentation. Augmentation covers more than multiplication, though. In mathematics, it's function
application: the base is the input, and the augment is the function. A number used as an augment
is treated (akin to casting, in programming) as the function that multiplies its input by that
number.
eis suk ei suk faf ous | What equals two 2s? na faf seip | It's 4. eis suk ei zoucsaum faf ous | What equals one-after 2? na faf sat | It's 3.
To negate, multiply by `craum`, or apply `juc`, which gives the negative
of its input. Negation is usually written with a forethought word like `um`, and so
"ou um" is a good sign that the operation will turn out to be subtraction. Division works the same way,
with `jamz` flipping numbers into their reciprocals. More generally, `jamz`
inverts functions, and since a number used as a function multiplies its input, its inverse divides.
sif ou um suk juc faf sat | 5 + −2 = 3 siaz ei um suk jamz faf sat | 6 × ½ = 3
Papsa has no order of operations. Mainstream notation multiplies before it adds, but Papsa reads afterthought phrases strictly left to right, like a calculator. To group things differently, use forethought words, which take a fixed number of items.
saum ou suk ei sat faf slark | (1 + 2) × 3 = 9 saum ou um suk sat faf slaux | 1 + 2 × 3 = 7
Variables are pronouns. Any pronoun will do, but mathematicians like to use ones that start
with "is-", for clarity. The most common are `isnu`, `isma`, and
`iski`, much like x, y, and z. Which pronouns are used in more complex expressions
depends on the field. The word for a variable (or assumption/pointer/placeholder) is
`tikpaun`.
Definitions use `paun`. Defined symbols are usually names rather than pronouns,
and they're commonly made by prefixing "ou-" to an evocative core word (often an elemental word,
in generic cases).
ouzus paun sif | Ouzus ≝ 5
The word for "function" is `mamzuip`. (The word for "algorithm" is
`mamtik`, and an arrow in category theory is a `mamas`). But in most
mathematical contexts, one simply uses `mam` ("transformation"). A clause with
`mam` as its verb is a lambda, with the variables in the subject slot and the expression
in the object slot. Lambdas are the normal way to define functions.
oubup paun li isnu mam isnu ou suk ei isnu | Oubup ≝ x ↦ (x + 2) x eis ous ei oubup faf slaf. sat | Oubup of what number equals 15? 3. ouzus ei oubup faf slix | Oubup(Ouzus) = 35 iau juc, faf li isnu mam isnu ei craum | "juc" = x ↦ x (−1)
Functions can also be defined point-free, using higher-order functions (which take functions
as input) and partial application. To partially apply addition or multiplication, use
`sis` and `suat`.
oujuc paun juc | Oujuc ≝ negate oupapsoun paun suk ei suat | Oupapsoun ≝ (2 ×) zoucsaum faf saum ei sis | successor = (1 +)
Addition and multiplication are commutative, so the order of their arguments doesn't much
matter, but not every function is so forgiving. For example, in mathematics `kan` is
the power function. As a rule, when a function can be written as a verb, its first argument is the
subject and its second is the object. So `suk ei kan` is the squaring function, and
`sat ei kan` is the cubing function.
suk kan sat ul faf slark | 3² = 9 (the subject is 2 and the object is 3) sat kan suk ul faf slurt | 2³ = 8 sat ei um suk kan faf slark | 3² = 9 (this time 3 comes first)
Since the base is the input and the augment is the function, Papsa math resembles reverse Polish
notation. That can be useful, but it can also be hard to follow, and it demands lots of words like
`um`. Association words like `ui` allow for more natural expressions,
without resorting to clauses. In mathematics they behave much like the matching augmentation words,
except that the first piece of content is the function and the later pieces are its inputs. Notably,
they feed those inputs in the opposite order from `ei`: object first, then
subject. (In some cases they can also change the interpretation of the function. See the following
sub-section on tensors for more.)
kau kan sat suk faf slark | 3² = 9 ouskuair paun suk ei kan | Ouskuair ≝ (^2) ukan paun kan ui skat | Ukan ≝ (e^)
The inverse (`jamz`) of a function with several arguments only inverts the
relationship between the subject slot and the output. So `kan ei jamz` is the logarithm:
given an output (the subject) and a base (the object), it returns the exponent. The logarithm with
base e has a name of its own.
kan ei jamz ui suk | log₂ kan ei jamz ui skat faf eijuiskat | logₑ = ln
TODO
TODO. Note the collision between the sum of entries in a list and the sum of two lists. (ui vs ei)
When a quantity can't be stated, it's either because counting is too much trouble, or because
the quantity is infinite. Papsa has words for both, typically used as augments (e.g. `ei`)
like an ordinary number. The two most important infinities are `suafou`
("infinity zero"), also known as ℵ₀, which is the cardinality of the set of integers,
and `iasuafou` ("six to the power of infinity zero"), which is the cardinality of the
uncountable continuum of real numbers.
guixuh ei suat | many stars (I can't be bothered to count) guixuh ei suatsuat | myriad stars (theoretically finite, but uncountable in practice) sip ei suaf | infinite set (vague) sip ei suafou | countably infinite set sip ei iasuafou | uncountably infinite set na sisuin suafou. iur am sisuin soun iasuafou | One can enumerate the elements of ℵ₀, but 𝔠 is uncountable.
To talk about limits, we use `kuh` (goes towards) and `nuif` (boundary).
sih kuh soun | The sequence approaches zero. soun nuif sih | Zero is the limit of the sequence. nai sih am nuif soun | The sequence has no limit (diverges). suaf nuif sih | The sequence diverges to infinity. sih kuh im suaf craum | The sequence goes to −∞.
TODO: derivatives, integrals
An infinitesimal is `haitsuaf`, "infinitely small".
TODO
The romanization used throughout this page is a convenience for English readers. Papsa also has a script of its own, designed from scratch rather than borrowed from any existing writing system. Any script has to balance two pairs of goals that pull against each other. Symbols that are easy to write are simple and uniform, and flow into each other, while symbols that are easy to read are contrasting and detailed. Likewise, a script that is accessible to everyone should have only a handful of letters, while a script that is inclusive of everyone should be able to write any name from any language faithfully.
Papsa resolves both tensions the same way. There is a small basic script, which is fast to write and captures only the broad sound-groups that nearly every language distinguishes, and a system of annotations that can be layered onto any letter to pin its sound down as precisely as needed (serving as a kind of phonetic alphabet). Basic text without annotations is sufficient for most uses, since the letters are unambiguous. Annotations are mostly important for names, to make script look fancier, or to help children learn to read. The expectation is that people jot things down in the basic script, and tech like keyboards and text editors fills in annotations from context wherever it'd help a reader.
For ergonomic reasons, letters are mostly vertical, words and lines run left to right, with the lines on a page being stacked top to bottom, with pages turned front to back, just like English.
Letters are naturally the same width, and almost all of them share a single horizontal baseline that runs through the middle. The shape of each letter is a hint to its sound, with big, smooth nasals, sharp plosives, soft fricatives, and vowels that share a common shape.
| Letter | Glyph | Stroke | |:------:|--------------------------------------------|---------------------------------------------------------------------------------------------| | `N` | | Curl up, cross down diagonally to make a loop, form a symmetrical loop, then return. | | `M` | | Curl up, cross down vertically to make a loop, then return diagonally. | | `K` | | Curl down, hook back, stop on the baseline, then return on baseline. | | `T` | | Curl up, hook back, go all the way down, then hook back again and curl into return. | | `P` | | Curl up sharply, stop half-way up, hook up, repeating, then hook back and curl into return. | | `F` | | Rise sharply, cross above, drop sharply back to baseline and return. | | `H` | | Cross on baseline. | | `C` | | Drop sharply, loop all the way around, returning to starting point, then cross on baseline. | | `S` | | Drop sharply, loop along the bottom as with `C`, but stop on baseline and return. | | `L` | | Descend diagonally, go back along the bottom, rise diagonally and cross to return. | | `U` | | Curl up, form a tight loop at the top, curl back down, then cross to make a bigger loop. | | `A` | | Curl up, then curl back down, crossing to make a big loop. | | `I` | | Curl up to midpoint, then curl back, forming a spike. | | `!` | | Lift pen. Draw vertical bar straight down the full height, in the middle. |
// Glyph code assumes a standard width of 360 units, and half-width spaces.
// Baseline is y=0. Negative y is up. Total height=792, thus ±396.
// These special numbers are assumed rather than defined as constants for the sake of readability.
// Strokes that are just a single x,y pair are straight lines.
// More commonly, strokes consisting of three x,y pairs are interpreted as Bézier curves.
const basicStrokes = {
N: [[180,72, 360,-360, 180,-360], [0,-360, 360,360, 180,360], [0,360, 180,-72, 360,0]],
M: [[360,-72, 360,-360, 180,-360], [0,-360, 0,468, 180,216], [360,0, 180,216, 360,0]],
K: [[144,0, 144,216, 72,324], [180,324, 360,0, 180,0], [360,0]],
T: [[180,-144, 180,-396, 36,-396], [360,-396, 144,324, 72,324], [108,324, 180,0, 360,0]],
P: [[288,-108, 288,-216, 108,-216], [288,-216, 180,-396, 36,-396], [360,-396, 180,0, 360,0]],
F: [[72,0, 144,-324, 36,-324], [0,-324, 72,-180, 180,-180], [288,-180, 360,-324, 324,-324], [216,-324, 288,0, 360,0]],
H: [[360,0]],
C: [[252,0, 36,-396, 72,144], [72,144, 72,396, 180,396], [360,396, 360,-396, 180,-396], [36,-396, 36,0, 180,0], [360,0]],
S: [[252,0, 36,-396, 72,144], [72,144, 72,396, 180,396], [288,396, 288,144, 288,144], [324,-396, 108,0, 360,0]],
L: [[360,396, 360,396, 180,396 ], [0,396, 0,396, 360,0]],
U: [[288,0, 288,-396, 180,-396], [108,-396, 108,-288, 180,-288], [252,-288, 252,-396, 180,-396], [72,-396, 72,0, 360,0]],
A: [[288,0, 288,-396, 180,-396], [ 72,-396, 72,0, 360,0]],
I: [[180,0, 180,-396, 180,-396], [180,-396, 180,0, 360,0]],
};
const letterStrokes = (ltr) => ltr === '!' ? {x:180, y:-396, segs:[[180,396]]} : {x:0, y:0, segs:basicStrokes[ltr]};
// A squeezed `U` has its big loop filled in (see below). The loop is where the path of the given curves
// (starting at the origin) crosses itself, and it's traced finely enough that the pen hides its corners.
const loopFill = (curves) => {
const pts = [[0, 0]];
for (const c of curves) {
const p = pts.at(-1);
for (let i = 1; i <= 64; i++) {
const t = i / 64, u = 1 - t, k = [u*u*u, 3*u*u*t, 3*u*t*t, t*t*t];
pts.push([0, 1].map(j => k[0]*p[j] + k[1]*c[j] + k[2]*c[2+j] + k[3]*c[4+j]));
}
}
const cross = (a, b) => a[0]*b[1] - a[1]*b[0], sub = (a, b) => [a[0]-b[0], a[1]-b[1]];
for (let j = 2; j < pts.length - 1; j++) for (let i = 0; i < j - 1; i++) {
const ab = sub(pts[i+1], pts[i]), cd = sub(pts[j+1], pts[j]), ac = sub(pts[j], pts[i]), den = cross(ab, cd);
const s = cross(ac, cd) / den, t = cross(ac, ab) / den;
if (s >= 0 && s < 1 && t >= 0 && t < 1) {
const [x, y] = [pts[i][0] + s*ab[0], pts[i][1] + s*ab[1]];
return { x, y, segs: [...pts.slice(i+1, j+1), [x, y]] };
}
}
fail("These curves don't form a loop.");
};
const squeezedFills = { U: loopFill([basicStrokes.U[0], basicStrokes.U[3]]) };
const segSVGCmd = (s) => s.length === 2 ? 'L' : s.length === 6 ? 'C' : fail("Unrecognized SVG Command: "+s);
// The w parameter is a scaling factor for the x-coordinate {0.5, 1}, that enables half-width vowels.
const segmentSVG = (s,x,y,w) => s.reduce((r,p,i) => r+' '+(i % 2 === 0 ? p*w + x : p + y), segSVGCmd(s));
const strokeSVG = (s,x,y,w) => s.segs.reduce((r,seg) => r+' '+segmentSVG(seg,x,y,w), ` M${s.x*w + x} ${s.y+y}`);
Every letter except the click enters at the left end of the baseline and leaves at the right
end, so the letters of a word join end to end and the whole word is written as a single
unbroken stroke. For example, here's `papsa`, written without lifting the pen:
PAPSA
Two-vowel diphthongs are very common in Papsa, and most fonts squeeze adjacent vowels together
so that words like `muk` and `mauk` take up the same width. When `U`
is written in a tight space, such as when squeezed like this, it's often rendered by filling in the
entire big loop, to better distinguish it from `A`.
MUK MAUK
Words are separated by a space. The click never touches the baseline, so writing it almost
always means lifting the pen, which suits a letter that almost only appears as its own word.
In the rare cases where a click shows up inside a word, such as the glottal stop in
`a'apaun`, a line along the baseline joins it to the letters on either side,
preserving the single stroke.
A'APAUN
Try reading the canonical Papsa pangram, `neim kud. pif xarc sou`:
NEIM KUD! PIF XARC SOU
// Adjacent vowels squeeze into one letter's width (so `muk` and `mauk` are equally long).
const letterWidth = (ltr,i,wrd) => isV(ltr) && (isV(wrd[i+1]) || isV(wrd[i-1])) ? 180 : 360;
const textPathData = (txt, diacriticLookup) => {
let letterPath = '', diacriticPath = '', fillPath = '', x = 0;
// Clicks written as ".", ",", or "!" stand apart like independent words.
for (const wrd of txt.match(/[.,!]|[^\s.,!]+/g) ?? []) {
if (x > 0) x += 180; // Word gap
// One array of characters (by code point), so each character and its alphabet index stay in step.
const chars = [...wrd], idxs = chars.map(ch => symbolShape(ch) ? undefined : letterIdx(ch));
let digits = 0; // How many digits in a row came just before. Every other one points down.
chars.forEach((ch, i) => {
const symbol = symbolShape(ch, digits % 2 === 1);
if (symbol) {
for (const s of symbol.strokes) letterPath += strokeSVG(s,x,0,1);
for (const f of symbol.fills) fillPath += strokeSVG(f,x,0,1) + ' Z';
// A digit's triangle reaches into the next letter's space, which only the next digit can share.
const isDigit = numeralValue(ch) !== undefined;
digits = isDigit ? digits + 1 : 0;
x += ! isDigit ? symbol.width ?? 360 : numeralValue(chars[i+1]) === undefined ? 720 : 360;
return;
}
digits = 0;
const baseLtr = baseLetter(ch);
const w = letterWidth(idxs[i], i, idxs);
letterPath += strokeSVG(letterStrokes(baseLtr),x,0,w/360);
if (w < 360 && squeezedFills[baseLtr]) fillPath += strokeSVG(squeezedFills[baseLtr],x,0,w/360) + ' Z';
for (const d of diacriticLookup(ch)) {
const a = d.anchor(baseLtr);
if (a) for (const s of d.strokes) diacriticPath += strokeSVG(s,x+a[0]*w/360,a[1],w/360);
}
x += w;
});
}
return { letterPath, diacriticPath, fillPath, width: x };
};
// Stroke weight is 36 units, with annotations being only 24. The vertical box is scriptExtent (worked
// out from the strokes in the Diacritic Drawing block), so every drawing shares it and baselines line up.
// (In this page's source, the tags below are written with < so the HTML parser leaves them as code.
// Unescaped, they became real elements and the script received the template with its markup missing.)
const glyphSVG = (txt, { letterPath, diacriticPath, fillPath, width }, size) => {
const pad = 18, { top, bottom } = scriptExtent;
const w = width + 2*pad, h = bottom - top;
const label = txt.replace(/[&<>"']/g, c => `${c.charCodeAt(0)};`); // Escape XML
return `<svg xmlns="http://www.w3.org/2000/svg" role="img" aria-label="${label} in Papsa script"
viewBox="${-pad} ${top} ${w} ${h}"
style="height:${size}em; width:${size * w / h}em; vertical-align:middle; overflow:visible"
fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round">
<title>${label}</title>
<path d="${letterPath}" stroke-width="36"/>
${fillPath && `<path d="${fillPath}" fill="currentColor" stroke="none"/>`}
${diacriticPath && `<path d="${diacriticPath}" stroke-width="24" opacity="0.5"/>`}
</svg>`;
};
Annotations are grouped by the aspects of sounds, such as voicing, place, channel, manner, and tongue posture. When an aspect is unspecified by any annotation (such as when a letter is totally unannotated), it's up to the reader to guess how that sound is pronounced from context and/or decide for themselves, based on what's easiest to them. Annotations are often drawn with a thinner or fainter line than the letter itself (or in a different color), so the basic letter underneath stays legible.
| Mark | Meaning | Examples | |----------------------------|----------------------------------------------|----------| | Small circle | Voiceless | | | Chevron | Voiced | | | Nested chevrons | Implosive (voiced, unless circled) | | | Overbar | Labial articulation group | | | Two upper bars | Coronal articulation group | | | Two lower bars | Dorsal articulation group | | | Underbar | Guttural articulation group | | | Vertical ticks on the bars | Articulation place within group (see below) | | | Open arc above | Approximant | | | Short stroke above | Tap | | | Zigzag above | Trill | | | Curves around the middle | Lateral | | | Solid square in the middle | Central | | | Rising swoosh below | Curled tongue (retroflex) | | | Arching swoosh below | Domed tongue (alveolo-palatal) | | | Both swooshes | Neutral tongue | |
Annotations, like letters, are designed to be mildly synesthetic. Voiceless sounds are
smooth, so they get a round mark, while voiced sounds vibrate, so they get a pointed one.
(Put a hand on your throat while saying "sss" and then "zzz" to feel the difference.)
The voicing marks sit near the vertical middle of a letter, in whichever space the letter
leaves empty, so they hang above `K` (which dips below the baseline) and below
`P` (which rises above it). `H` carries its mark above the line,
leaving space below for its place bars.
Implosives, a linguistically rare sound, are indicated with a second chevron nested against the voiced one. This is an exception to every mark pinning down a single aspect: since implosives are almost always voiced, the nested chevrons mean "implosive, and voiced by default." (Add a voiceless circle on top to make voiceless implosives.) The voiced mark alone doesn't rule out an implosive, but since they're rare, it's normal to avoid them unless explicit.
Overbars/underbars identify the broad region of the mouth where the sound is made, known as the articulation group. To specify an even more precise place (rare), one or two vertical ticks are added. A left tick indicates the front place, right indicates the back, and a left and right tick together indicate the place in between.
| Group | Bars | Side | Places (front to back) | Examples | |----------|------|-------|---------------------------------|----------| | labial | 1 | above | bilabial, labiodental | | | coronal | 2 | above | dental, alveolar, postalveolar | | | dorsal | 2 | below | palatal, velar, uvular | | | guttural | 1 | below | pharyngeal, epiglottal, glottal | |
The sounds of `L` are the most diverse. Manner is marked above the line: an open
arc indicates an approximant (no contact), a short stroke for a tap (one brief contact), and a
zigzag for a trill (repeated contact). The other letters already fix their manner, so these marks
only appear on `L`.
Sounds made with the tip or blade of the tongue can differ in how the tongue is shaped, shown by a swoosh below the letter. A curled (retroflex) tongue gets a swoosh that rises to the right, like the tip curling up and back. A domed (alveolo-palatal) tongue gets its mirror image, arching like the raised blade. A neutral tongue gets both at once. Posture implies a coronal place, and coronal sounds never use the lower bars, so the space below is always free for it.
Vowels take the same bars as consonants, but for height: the higher the tongue, the higher the
bars. Ticks on the bars give backness, the same way they pick a place within a consonant's group:
a left tick for a front vowel, a right tick for a back one, and both for a central one. Just under
the baseline, a circle marks rounded lips and a chevron unrounded ones. Lower down, a sideways
chevron gives tenseness. A lax vowel sits half a step nearer the middle than its bars say, so
ɪ is a lax close vowel and ə a lax close-mid one. Tense means the vowel
sits right at its bars' height. When vowels are squeezed together, each keeps its marks in the same
places, at half width.
| Height | Bars | Side | Example | |-----------|------|-------|---------| | close | 1 | above | | | close-mid | 2 | above | | | open-mid | 2 | below | | | open | 1 | below | |
| Mark | Meaning | Examples | |--------------------------|----------------------------------------|----------| | Vertical ticks on bars | Backness (front, central, back) | | | Circle | Rounded | | | Downward chevron | Unrounded | | | Right-pointing chevron | Tense | | | Left-pointing chevron | Lax | |
As with consonants, each IPA vowel carries the fewest marks that tell it apart from the other
vowels of its letter. In many contexts vowels go unannotated, and the romanized `u`,
`a`, and `i` are always drawn bare. Here are the most common vowel
sounds of English:
| Sound (IPA) | As in... | Letter | Glyph | |:-----------:|------------------|----------|:-----:| | ɪ | "i" in "sit" | `I` | | | i | "ea" in "seat" | `I` | | | ə | "u" in "upon" | `A` | | | ɛ | "e" in "dress" | `A` | | | æ | "a" in "cat" | `A` | | | ʌ | "u" in "bun" | `A` | | | ɑ | "a" in "palm" | `A` | | | u | "ou" in "soup" | `U` | | | ʊ | "oo" in "foot" | `U` | | | aɪ | "ie" in "pie" | `A`, `I` | | | eɪ | "ay" in "may" | `I`, `I` | | | oʊ | "oa" in "goat" | `U`, `U` | | | aʊ | "ow" in "how" | `A`, `U` | |
Glides like the ones at the start of "yes" and "west" and affricates like the ones in "jump" and "chew" don't get letters of their own. They're built from existing letters (as with the "dj" in Phonology), and annotated if a collision needs clearing up.
Computers don't have Papsa keyboards (yet!), so we need an encoding scheme to map familiar
symbols to Papsa symbols. We'll discuss this more in the Font subsection, below. But regardless
of the specific method, it's typical to relate letters to the least-annotated Papsa glyph available.
Specific IPA characters, for example, use the fewest marks that tell it apart from the other sounds
of its Papsa letter. Romanized letters get fewer still: just enough to tell them apart from each other,
which covers the distinctions an English reader needs (`k` from `g`,
`n` from `ŋ`, and so on). Anything narrower is written with the combining
marks defined in the Font section.
| Sound | Letter | Phonation | Place | Channel | Manner | Posture | Glyph | |:-----:|--------|-----------|--------------|---------|-------------|---------|-------| | n | N | voiced | alveolar | | | | | | ŋ | N | voiced | velar | | | | | | ɲ | N | voiced | palatal | | | | | | ɳ | N | voiced | postalveolar | | | curl | | | ɴ | N | voiced | uvular | | | | | | m | M | voiced | bilabial | | | | | | ɱ | M | voiced | labiodental | | | | | | k | K | voiceless | velar | | | | | | g | K | voiced | velar | | | | | | q | K | voiceless | uvular | | | | | | ɢ | K | voiced | uvular | | | | | | ɠ | K | implosive | velar | | | | | | ʛ | K | implosive | uvular | | | | | | t | T | voiceless | alveolar | | | | | | d | T | voiced | alveolar | | | | | | ʈ | T | voiceless | postalveolar | | | curl | | | ɖ | T | voiced | postalveolar | | | curl | | | c | T | voiceless | palatal | | | | | | ɟ | T | voiced | palatal | | | | | | ɗ | T | implosive | alveolar | | | | | | ᶑ | T | implosive | postalveolar | | | curl | | | ʄ | T | implosive | palatal | | | | | | p | P | voiceless | bilabial | | | | | | b | P | voiced | bilabial | | | | | | ɓ | P | implosive | bilabial | | | | | | f | F | voiceless | labiodental | | | | | | v | F | voiced | labiodental | | | | | | θ | F | voiceless | dental | | | | | | ð | F | voiced | dental | | | | | | ɸ | F | voiceless | bilabial | | | | | | β | F | voiced | bilabial | | | | | | h | H | voiceless | glottal | | | | | | ɦ | H | voiced | glottal | | | | | | x | H | voiceless | velar | | | | | | ɣ | H | voiced | velar | | | | | | χ | H | voiceless | uvular | | | | | | ʁ | H | voiced | uvular | | | | | | ħ | H | voiceless | pharyngeal | | | | | | ʕ | H | voiced | pharyngeal | | | | | | ʜ | H | voiceless | epiglottal | | | | | | ʢ | H | voiced | epiglottal | | | | | | ʃ | C | voiceless | postalveolar | central | | neutral | | | ʒ | C | voiced | postalveolar | central | | neutral | | | ç | C | voiceless | palatal | central | | | | | ʝ | C | voiced | palatal | central | | | | | ʂ | C | voiceless | postalveolar | central | | curl | | | ʐ | C | voiced | postalveolar | central | | curl | | | ɕ | C | voiceless | postalveolar | central | | domed | | | ʑ | C | voiced | postalveolar | central | | domed | | | ɬ | C | voiceless | alveolar | lateral | | | | | ɮ | C | voiced | alveolar | lateral | | | | | s | S | voiceless | alveolar | | | | | | z | S | voiced | alveolar | | | | | | l | L | voiced | alveolar | lateral | approximant | | | | ɭ | L | voiced | postalveolar | lateral | approximant | curl | | | ʎ | L | voiced | palatal | lateral | approximant | | | | ʟ | L | voiced | velar | lateral | approximant | | | | ɹ | L | voiced | alveolar | central | approximant | | | | ɻ | L | voiced | postalveolar | central | approximant | curl | | | ɰ | L | voiced | velar | central | approximant | | | | r | L | voiced | alveolar | central | trill | | | | ʀ | L | voiced | uvular | central | trill | | | | ɾ | L | voiced | alveolar | central | tap | | | | ɽ | L | voiced | postalveolar | central | tap | curl | | | ǀ | ! | voiceless | dental | central | | | | | ǃ | ! | voiceless | alveolar | central | | | | | ǁ | ! | voiceless | alveolar | lateral | | | | | ǂ | ! | voiceless | palatal | central | | | | | ʘ | ! | voiceless | bilabial | | | | | | ʔ | ! | voiceless | glottal | | | | | | ʡ | ! | voiceless | epiglottal | | | | |
| Sound | Letter | Height | Backness | Rounding | Tenseness | Glyph | |:-----:|--------|-----------|----------|-----------|-----------|-------| | i | I | close | front | unrounded | tense | | | ɪ | I | close | front | unrounded | lax | | | e | I | close-mid | front | unrounded | tense | | | ɨ | I | close | central | unrounded | tense | | | u | U | close | back | rounded | tense | | | ʊ | U | close | back | rounded | lax | | | o | U | close-mid | back | rounded | tense | | | ɯ | U | close | back | unrounded | tense | | | ɤ | U | close-mid | back | unrounded | tense | | | y | U | close | front | rounded | tense | | | ʏ | U | close | front | rounded | lax | | | ø | U | close-mid | front | rounded | tense | | | ʉ | U | close | central | rounded | tense | | | ɵ | U | close-mid | central | rounded | tense | | | a | A | open | front | unrounded | tense | | | æ | A | open | front | unrounded | lax | | | ɶ | A | open | front | rounded | tense | | | ä | A | open | central | unrounded | tense | | | ɐ | A | open | central | unrounded | lax | | | ɑ | A | open | back | unrounded | tense | | | ɒ | A | open | back | rounded | tense | | | ɛ | A | open-mid | front | unrounded | tense | | | œ | A | open-mid | front | rounded | tense | | | ɜ | A | open-mid | central | unrounded | tense | | | ɞ | A | open-mid | central | rounded | tense | | | ʌ | A | open-mid | back | unrounded | tense | | | ɔ | A | open-mid | back | rounded | tense | | | ɘ | A | close-mid | central | unrounded | tense | | | ə | A | close-mid | central | unrounded | lax | |
// Annotation marks, each drawn relative to an anchor point on the letter it annotates (and not drawn
// at all on a letter with no anchor for it). Every consonant mark pins down one aspect of a sound; see
// the Annotations section for the system.
const isConsonantLetter = (l) => ! vowelChars.has(l);
const onConsonants = (a) => (l) => isConsonantLetter(l) ? a : undefined;
const onVowels = (a) => (l) => vowelChars.has(l) ? a : undefined;
const ring = { x: 0, y: 0, segs: [[0,-72, 108,-72, 108,0], [108,72, 0,72, 0,0]] };
// Phonation hangs in each letter's empty space: above the line on H, and below it on the click.
const voiceAnchor = { N: [234,-170], M: [262,210], K: [126,-108], T: [28,64], P: [126,54], F: [126,54],
H: [126,-108], C: [126,108], S: [126,54], L: [126,-108], '!': [216,200] };
const chevron = (dy) => ({ x: 0, y: dy, segs: [[54, 54 + dy], [108, dy]] });
// The implosive's second chevron sits away from the letter body: above the first on K, below it elsewhere.
const implosiveAnchor = (l) => voiceAnchor[l] && [voiceAnchor[l][0], voiceAnchor[l][1] + (l === 'K' ? 0 : 54)];
// Place: bars name the group and ticks pick the place within it (see the placeGroups table). A single bar
// sits at the letter's edge, and a double bar adds a second one 120 units further in.
const placeEdge = 396, placeGap = 120, tickReach = 42, tickX = [72, 288];
const placeGroupList = [...placeGroups['Group']].map(([name, row]) => ({
name, bars: +row['Bars'], side: row['Side'] === 'above' ? -1 : 1,
places: row['Places (front to back)'].split(',').map(p => p.trim()),
}));
const barDistances = (g) => g.bars === 1 ? [placeEdge] : [placeEdge, placeEdge - placeGap]; // Outer bar first.
// H's bars sit closer to the baseline. The click's lower bars are centered on the baseline, so its guttural
// bar (and with it the glottal stop) runs along it.
const placeShift = (l, g) => l === 'H' ? -g.side * 144
: l === '!' && g.side > 0 ? -barDistances(g).reduce((a, b) => a + b) / g.bars : 0;
// Group bars go on every letter (they give height on a vowel), but ticks and full places are for consonants.
const barAnchor = (g) => (l) => [0, placeShift(l, g)];
const placeAnchor = (g) => (l) => isConsonantLetter(l) ? barAnchor(g)(l) : undefined;
const barStrokes = (g) => barDistances(g).map(d => ({ x: 36, y: g.side * d, segs: [[324, g.side * d]] }));
// A tick crosses every bar of its group, at the front (0) or back (1) end.
const tickStroke = (g, end) => {
const ds = barDistances(g), x = tickX[end];
return { x, y: g.side * (ds[0] + tickReach), segs: [[x, g.side * (ds.at(-1) - tickReach)]] };
};
const ticksFor = (i) => [[0], [0, 1], [1]][i]; // Front, middle (both ticks), back.
const defaultTickGroup = placeGroupList.find(g => g.side < 0 && g.bars === 2) ?? placeGroupList[0];
// Central is a solid square, drawn as a tight zigzag so the round pen fills it in.
const squareSegs = [];
for (let k = 0; k <= 6; k++) {
const y = -60 + 20 * k;
if (k) squareSegs.push([k % 2 ? 240 : 120, y]);
squareSegs.push([k % 2 ? 120 : 240, y]);
}
const glyphMarks = {
voiceless: { anchor: l => voiceAnchor[l], strokes: [ring] },
voiced: { anchor: l => voiceAnchor[l], strokes: [chevron(0)] },
implosive: { anchor: implosiveAnchor, strokes: [chevron(0), chevron(-54)] },
// The tick characters on their own, drawn for a double bar above. The font redraws them to match the
// group bar they're typed with (see the Font Builder).
frontTick: { anchor: placeAnchor(defaultTickGroup), strokes: [tickStroke(defaultTickGroup, 0)] },
backTick: { anchor: placeAnchor(defaultTickGroup), strokes: [tickStroke(defaultTickGroup, 1)] },
// Channel, centered at (180,0).
lateral: { anchor: onConsonants([0, 0]), strokes: [{ x: 126, y: -66, segs: [[90,-40, 90,40, 126,66]] },
{ x: 234, y: -66, segs: [[270,-40, 270,40, 234,66]] }] },
central: { anchor: onConsonants([0, 0]), strokes: [{ x: 120, y: -60, segs: squareSegs }] },
// Manner, above the line on L.
approximant: { anchor: l => l === 'L' ? [180, -180] : undefined, strokes: [{ x: -114, y: -24, segs: [[-66,48, 66,48, 114,-24]] }] },
tap: { anchor: l => l === 'L' ? [180, -180] : undefined, strokes: [{ x: 0, y: -48, segs: [[0,48]] }] },
trill: { anchor: l => l === 'L' ? [180, -180] : undefined, strokes: [{ x: -120, y: 0, segs: [[-60,-36], [0,36], [60,-36], [120,0]] }] },
// Coronal posture, a swoosh below the letter. Neutral is curl and domed drawn together.
curl: { anchor: onConsonants([0, 474]), strokes: [{ x: 36, y: 0, segs: [[120,54, 240,48, 324,-42]] }] },
domed: { anchor: onConsonants([0, 474]), strokes: [{ x: 36, y: 0, segs: [[120,-54, 240,-48, 324,42]] }] },
// Vowel marks below the line (height and backness are bars and ticks, below). Rounding sits just under
// the baseline, and tenseness lower, centered where a pair of underbars would flank it.
rounded: { anchor: onVowels([126, 96]), strokes: [ring] },
unrounded: { anchor: onVowels([126, 69]), strokes: [chevron(0)] },
tense: { anchor: onVowels([180, (placeEdge * 2 - placeGap) / 2]), strokes: [{ x: -96, y: -45, segs: [[96,0], [-96,45]] }] },
lax: { anchor: onVowels([180, (placeEdge * 2 - placeGap) / 2]), strokes: [{ x: 96, y: -45, segs: [[-96,0], [96,45]] }] },
};
glyphMarks.neutral = { anchor: glyphMarks.curl.anchor, strokes: [...glyphMarks.curl.strokes, ...glyphMarks.domed.strokes] };
// Each group is a mark (its bars), and so is each place (the bars plus its ticks). A composite mark is typed
// as the combining characters that make it up.
const placeOf = {}; // place name -> { group, index }
const markSpelling = { neutral: ['curl', 'domed'] };
for (const g of placeGroupList) {
if (g.places.length > 3) reportSpecError('Diacritic Drawing', `The ${g.name} group has more places than ticks can tell apart.`);
glyphMarks[g.name] = { anchor: barAnchor(g), strokes: barStrokes(g) };
g.places.forEach((p, i) => {
placeOf[p] = { group: g, index: i };
glyphMarks[p] = { anchor: placeAnchor(g), strokes: [...barStrokes(g), ...ticksFor(i).map(e => tickStroke(g, e))] };
markSpelling[p] = [g.name, ...ticksFor(i).map(e => ['frontTick', 'backTick'][e])];
});
}
// ── Sound features ──
// Every sound in the alphabet, described by the aspects the marks can pin down.
const featureDims = ['Phonation', 'Place', 'Channel', 'Manner', 'Posture'];
const vowelDims = ['Height', 'Backness', 'Rounding', 'Tenseness'];
// A vowel's height is drawn with the place group that has the same bars.
const heightMark = {};
for (const [h, row] of vowelHeights['Height']) {
const g = placeGroupList.find(g => g.bars === +row['Bars'] && (g.side < 0) === (row['Side'] === 'above'));
if (! g) { reportSpecError('Diacritic Drawing', `No place group has the bars given for ${h} vowels.`); continue; }
heightMark[h] = g.name;
// Backness is the group's ticks crossing the height bars: front, central (both), or back.
['front', 'central', 'back'].forEach((b, i) => {
glyphMarks[`${h}-${b}`] = { anchor: l => vowelChars.has(l) ? barAnchor(g)(l) : undefined,
strokes: [...barStrokes(g), ...ticksFor(i).map(e => tickStroke(g, e))] };
markSpelling[`${h}-${b}`] = [g.name, ...ticksFor(i).map(e => ['frontTick', 'backTick'][e])];
});
}
const featureValues = { Phonation: ['voiceless', 'voiced', 'implosive'], Place: Object.keys(placeOf),
Channel: ['lateral', 'central'], Manner: ['approximant', 'tap', 'trill'], Posture: ['curl', 'neutral', 'domed'],
Height: Object.keys(heightMark), Backness: ['front', 'central', 'back'], Rounding: ['rounded', 'unrounded'],
Tenseness: ['tense', 'lax'] };
const featureOf = {}; // IPA sound -> { sound, letter, dims, Phonation, Place, ... }
for (const [table, dims] of [[consonantFeatures, featureDims], [vowelFeatures, vowelDims]]) {
for (const [sound, row] of table['Sound']) {
const f = { sound, letter: row['Letter'], dims };
for (const d of dims) {
if (row[d] && ! featureValues[d].includes(row[d])) reportSpecError('Diacritic Drawing', `${sound} has "${row[d]}" for ${d.toLowerCase()}, which isn't one of its values.`);
f[d] = row[d] || undefined;
}
const listed = alphabet['Acceptable sounds (IPA)'][sound]?.['Letter'];
if (listed && listed !== f.letter) reportSpecError('Diacritic Drawing', `The alphabet puts ${sound} under ${listed}, not ${f.letter}.`);
featureOf[sound] = f;
}
}
for (const [ltr, row] of alphabet['Letter']) {
for (const s of row['Acceptable sounds (IPA)'].split(',').map(x => x.trim()))
if (! featureOf[s]) reportSpecError('Diacritic Drawing', `${s} (${ltr}) has no row in the ${isConsonantLetter(ltr) ? 'consonant' : 'vowel'} table.`);
}
// The fewest marks that single a sound out of a pool, or undefined if none do. A full place costs two (bar and
// tick), a group one, and anything else one each; ties go to the spelling that touches fewer aspects. Implosive
// counts as its own phonation here: the voiced mark doesn't rule implosives out, but implosives always carry
// their own mark. Place (or a vowel's height) comes last in the list, so the font can match ticks to their bar
// (see Font Builder).
const minimalMarks = (f, pool) => {
const dims = f.dims;
let best;
const visit = (i, spec) => {
if (i === dims.length) {
const hits = pool.filter(o => dims.every(d => ! spec[d] || (d !== 'Place' ? o[d] === f[d]
: spec[d] === 1 ? placeOf[o.Place]?.group === placeOf[f.Place].group : o.Place === f.Place)));
// Like a consonant's place ticks, a vowel's backness ticks need height bars to cross.
if (hits.length !== 1 || (spec.Backness && ! spec.Height)) return;
const cost = dims.reduce((c, d) => c + (spec[d] || 0), 0), touched = dims.filter(d => spec[d]).length;
if (! best || cost < best.cost || (cost === best.cost && touched < best.touched)) best = { spec: { ...spec }, cost, touched };
return;
}
const d = dims[i];
for (const v of d === 'Place' ? [0, 1, 2] : [0, 1]) {
if (v && ! f[d]) continue;
spec[d] = v;
visit(i + 1, spec);
}
};
visit(0, {});
if (! best) return;
const s = best.spec;
return [...dims.filter(d => ! ['Place', 'Height', 'Backness'].includes(d) && s[d]).map(d => f[d]),
s.Place === 1 ? placeOf[f.Place].group.name : s.Place === 2 ? f.Place : null,
s.Height && (s.Backness ? `${f.Height}-${f.Backness}` : heightMark[f.Height])].filter(Boolean);
};
// IPA characters take the fewest marks that tell them apart within their letter.
const letterPools = {};
for (const f of Object.values(featureOf)) (letterPools[f.letter] ??= []).push(f);
const ipaMarks = {};
for (const f of Object.values(featureOf)) {
ipaMarks[f.sound] = minimalMarks(f, letterPools[f.letter])
?? (reportSpecError('Diacritic Drawing', `${f.sound} can't be told apart from the other sounds of ${f.letter}.`), []);
}
// The marks each lowercase character is drawn with. Uppercase letters are drawn bare, and so are the
// characters not listed (m, ...).
const charMarks = {
// The click's grammatical forms. The sentence divider is left bare, since its pronunciation varies. The
// quote closer is drawn as a palatal click, and the click inside a word takes the guttural bar that joins it
// to its neighbors.
'.': [], ',': ipaMarks['ǂ'], "'": ['guttural'],
};
// Romanized letters take the fewest marks that tell them apart from the other romanized letters of their
// Papsa letter, which are the distinctions an English reader needs. The romanized vowels are the ones the
// diphthongs are spelled with, read as the IPA vowel of the same name. Of these, u, a, and i stay bare.
const romanPools = {};
const romanVowels = new Set([...engDiphthongRomanization['Diphthong']].flatMap(([d]) => [...d]));
for (const [r, sound] of [...[...engConsonantRomanization['Romanization']].map(([r, row]) => [r, row['Sound (IPA)']]),
...[...romanVowels].map(v => [v, v])]) {
const f = featureOf[sound];
if (f) (romanPools[f.letter] ??= []).push([r, f]);
else reportSpecError('Diacritic Drawing', `The romanized ${r} stands for ${sound}, which has no row in the sound tables.`);
}
for (const pool of Object.values(romanPools)) for (const [r, f] of pool) {
charMarks[r] = vowelChars.has(r.toUpperCase()) ? [] : minimalMarks(f, pool.map(([, g]) => g))
?? (reportSpecError('Diacritic Drawing', `The romanized ${r} can't be told apart from the other romanized letters.`), []);
}
// Every other IPA character uses its own minimal marks. (Where an IPA character doubles as a romanized
// letter, such as r, the romanized reading wins.)
for (const [sound, marks] of Object.entries(ipaMarks)) if (! (sound in charMarks)) charMarks[sound] = marks;
// A mark with no anchor on its letter would silently vanish, so check them all.
for (const [ch, marks] of Object.entries(charMarks)) for (const m of marks) {
if (! glyphMarks[m]) reportSpecError('Diacritic Drawing', `${ch} uses "${m}", which isn't a mark.`);
else if (! glyphMarks[m].anchor(baseLetter(ch))) reportSpecError('Diacritic Drawing', `"${m}" has no place on ${baseLetter(ch)}, so ${ch} would lose it.`);
}
// How far the script reaches above and below the baseline, sampled from the strokes themselves with half
// the pen width added. Marks can reach past the letters' ±396 (a vowel's overbar does), so the drawings
// here and the font's vertical metrics both size themselves from this instead of assuming.
const scriptExtent = (() => {
let top = Infinity, bottom = -Infinity;
const see = (stroke, y0, halfPen) => {
const mark = (y) => { top = Math.min(top, y + y0 - halfPen); bottom = Math.max(bottom, y + y0 + halfPen); };
let py = stroke.y;
mark(py);
for (const seg of stroke.segs) {
if (seg.length === 2) mark(seg[1]);
else for (let t = 1/32; t <= 1; t += 1/32) {
const u = 1 - t;
mark(u*u*u*py + 3*u*u*t*seg[1] + 3*u*t*t*seg[3] + t*t*t*seg[5]);
}
py = seg.at(-1);
}
};
for (const ltr of [...Object.keys(basicStrokes), '!']) {
see(letterStrokes(ltr), 0, 18);
for (const m of Object.values(glyphMarks)) {
const a = m.anchor(ltr);
if (a) for (const s of m.strokes) see(s, a[1], 12);
}
}
return { top, bottom };
})();
Papsa's number words are naturally compact and easy to write. Writing one-hundred-thirteen as 113
takes three strokes, while `slifat` takes one fluid stroke covering only about twice as
much horizontal space. In places where numbers are expected (equations, spreadsheets, etc.), one can
use `ifat` to be almost as compact. When writing numbers on a page or a whiteboard, that's
the norm. But numbers are everywhere, and it's still useful to have numerals that are optimized for
being readable and clear in situations where letters aren't as good, at the expense of being harder
to write (when not at a computer).
Each compact digit from 0 to 35 has a numeral that fits in an equilateral triangle. The first digit in a number points up, the next digit points down, and so on, so that each triangle nests between its neighbors and a row of digits is dense and readable as a single number.
| | +0 | +1 | +2 | +3 | +4 | +5 | |:-----:|:-----:|:-----:|:-----:|:-----:|:-----:|:-----:| | 0 | | | | | | | | 6 | | | | | | | | 12 | | | | | | | | 18 | | | | | | | | 24 | | | | | | | | 30 | | | | | | |
We can symbolize the non-numeric parts of number words as well. The radix mark ("-aih-"), for example, is drawn with a clockwise spiral and the exponent mark ("-iaz-") is a hexagon that's split into six triangles. Marks like this reset things so the first following digit points upward. Negative numbers start with a symbol indicating "less than zero", which the negative exponent mark inherits.
| Papsa Word | Numerals | Value | |------------|----------|-------| | `slifat` | | 5 + 3×36 = one-hundred-thirteen | | `slaktuzixat` | | one-hundred-eighty-six-thousand, two-hundred-eighty-two | | `slataihifatakt` | | 3 + 5/36 + 3/36² + 18/36³ ≈ 3.14159 (π, approximately) | | `slukaihuzukiazikt` | | (2 + 26/36 + 2/36²) × 6³⁰ ≈ 6.022 × 10²³ | | `sleipaihaugixiajirk` | | (4 + 19/36 + 35/36²) × 6⁻²⁵ ≈ 1.602 × 10⁻¹⁹ | | `craumiajat` | | −1 × 6⁻³ = −1/216 |
// Every numeral fits in an equilateral triangle (the frame, which is only drawn as part of some digits) two
// letters wide and centered on the baseline. Digits are drawn here pointing up. Pointing down is the same
// shape flipped over the baseline. A shape has strokes for the pen and filled areas (the pips), in the same
// units as the letters.
const FRAME_H = 360 * Math.sqrt(3);
const frameCorners = [[360, -FRAME_H/2], [720, FRAME_H/2], [0, FRAME_H/2]]; // Top, then clockwise.
const frameCenter = [360, FRAME_H/6];
const towards = (p, q, t) => [p[0] + (q[0] - p[0]) * t, p[1] + (q[1] - p[1]) * t];
const polyline = (pts) => ({ x: pts[0][0], y: pts[0][1], segs: pts.slice(1) });
const circle = ([cx, cy], r) => { // Four Bézier arcs, for the pen.
const k = 0.5523 * r;
return { x: cx + r, y: cy, segs: [[cx+r, cy+k, cx+k, cy+r, cx, cy+r], [cx-k, cy+r, cx-r, cy+k, cx-r, cy],
[cx-r, cy-k, cx-k, cy-r, cx, cy-r], [cx+k, cy-r, cx+r, cy-k, cx+r, cy]] };
};
const pip = ([cx, cy]) => polyline(Array.from({ length: 32 }, (_, k) => [cx + 30 * Math.cos(k * Math.PI / 16), cy + 30 * Math.sin(k * Math.PI / 16)]));
const digitShape = (n) => {
const c = frameCenter, strokes = [], fills = [];
const sides = frameCorners.map((v, i) => towards(frameCorners[(i+1) % 3], frameCorners[(i+2) % 3], 0.5)); // Across from each corner
const inner = sides.map(m => towards(c, m, 124 / Math.hypot(m[0] - c[0], m[1] - c[1]))); // Two's triangle points at the sides.
if (n === 0) strokes.push(circle(c, 168));
if (n >= 24) strokes.push(polyline([...frameCorners, frameCorners[0]]), ...sides.map((m, i) => polyline([m, inner[i]])));
else if (n >= 12) strokes.push(...frameCorners.map((v, i) =>
polyline([towards(v, frameCorners[(i+1) % 3], 0.3), v, towards(v, frameCorners[(i+2) % 3], 0.3)])));
const threes = Math.floor(n % 12 / 3), ones = n % 3;
for (const v of frameCorners) {
if (threes % 2) fills.push(pip(towards(c, v, 0.64))); // Three: a pip in each corner
if (threes >= 2) { // Six: a pair of pips near each corner
const p = towards(c, v, 0.4), s = 58 / Math.hypot(v[0] - c[0], v[1] - c[1]), d = [(c[1] - v[1]) * s, (v[0] - c[0]) * s];
fills.push(pip([p[0] + d[0], p[1] + d[1]]), pip([p[0] - d[0], p[1] - d[1]]));
}
}
if (ones) fills.push(pip(c)); // One
if (ones === 2) strokes.push(polyline([...inner, inner[0]])); // Two
return { strokes, fills };
};
// The marks that go between and before digits are a letter wide.
const hexagon = Array.from({ length: 7 }, (_, k) => [180 + FRAME_H/4 * Math.sin(k * Math.PI / 3), -FRAME_H/4 * Math.cos(k * Math.PI / 3)]);
const exponentMark = [polyline(hexagon), ...[0, 1, 2].map(k => polyline([hexagon[k], hexagon[k + 3]]))];
// "Less than zero": a circle, and a ">" made of a stem rising from the hexagon's right side and its top-right edge.
const belowZero = (dy) => [circle([180, -283 + dy], 80), polyline([[hexagon[1][0], -250 + dy], [hexagon[1][0], hexagon[1][1] + dy]])];
const spiral = ([cx, cy], r0, r1, quarters) => { // Clockwise from the top, shrinking from r0 to r1, in Bézier quarter turns.
const at = (q) => {
const a = q * Math.PI / 2, r = r0 + (r1 - r0) * q / quarters, dr = (r1 - r0) / quarters * 2 / Math.PI;
return { p: [cx + r * Math.sin(a), cy - r * Math.cos(a)], d: [dr * Math.sin(a) + r * Math.cos(a), r * Math.sin(a) - dr * Math.cos(a)] };
};
return Array.from({ length: quarters }, (_, q) => {
const a = at(q), b = at(q + 1);
return [a.p[0] + 0.5523 * a.d[0], a.p[1] + 0.5523 * a.d[1], b.p[0] - 0.5523 * b.d[0], b.p[1] - 0.5523 * b.d[1], ...b.p];
});
};
const numberMarkShapes = {
';': { strokes: [{ x: 20, y: -160, segs: [[190, -160], ...spiral([190, 0], 160, 30, 7)] }], fills: [] },
'₆': { strokes: exponentMark, fills: [] },
'₋': { strokes: [...exponentMark, ...belowZero(0)], fills: [] },
'-': { strokes: [...belowZero(220), polyline([hexagon[1], hexagon[0]].map(([x, y]) => [x, y + 220]))], fills: [] },
};
// Digits are encoded as 0 to 9, or as the circled numbers ⓪ to ㉟.
const circledDigits = Array.from({ length: 36 }, (_, n) => String.fromCodePoint(n === 0 ? 0x24EA : n <= 20 ? 0x245F + n : 0x323C + n));
const numeralValue = (ch) => circledDigits.includes(ch) ? circledDigits.indexOf(ch) : /^[0-9]$/.test(ch ?? '') ? +ch : undefined;
const digitShapes = circledDigits.map((_, n) => digitShape(n));
const flipStroke = (s) => ({ x: s.x, y: -s.y, segs: s.segs.map(seg => seg.map((v, i) => i % 2 ? -v : v)) });
// A number mark's shape, or a digit's (pointing down if asked), or undefined for any other character.
// (Use symbolShape, below, which also knows the shorthand symbols.)
const numberSymbol = (ch, down) => {
const n = numeralValue(ch);
if (n === undefined) return numberMarkShapes[ch];
const shape = digitShapes[n];
return down ? { strokes: shape.strokes.map(flipStroke), fills: shape.fills.map(flipStroke) } : shape;
};
Papsa also has shorthand symbols for words that are especially common in math. Unlike the numerals, they're meant to be easy to write by hand, and often stand for their words outside of math contexts, too. Parentheses stand for the clause marks, taking whichever slot is implied (and like the clause marks they don't need to be balanced).
| Papsa Word | Character | Symbol | |-------------------------|-----------|-------------------------------------------------| | `faf` | = | | | `fafjuc` | ≠ | | | `zousis` | < | | | `zousfaf` | ≤ | | | `zousfaf ei jamz` | ≥ | | | `zousis ei jamz` | > | | | `ou` | + | | | `ei` | * | | | `paun` | ≝ | | | `mam` | ↦ | | | `lu`, `la`, `li` | ( | | | `ul`, `al`, `il` | ) | |
The variable mark (~) goes in front of a letter and changes how that letter
is read as a pronoun. Alone, `N` reads as `ana`, but with a variable mark,
it usually reads as `isnu` (or another pronoun that fits the context).
| Papsa | Symbols | Script | |------------------------------------------------|-------------------------------|-------------------------------------------------------------------| | `oubup paun li isnu mam isnu ou suk ei isnu` | `oubup ≝ (~n ↦ ~n + ② * ~n` | | | `suk zousfaf ei jamz soun` | `② ≥ ⓪` | | | `slataihifatakt zousis sleip` | `③;⑤③⑱ < ④` | | | `craumiajat zousis soun` | `-①₋③ < ⓪` | |
// The shorthand symbols are each a letter wide (unless they give a width), and centered on the baseline.
const mathSymbolShapes = (() => {
const mirror = (s) => ({ x: 360 - s.x, y: s.y, segs: s.segs.map(seg => seg.map((v, i) => i % 2 ? v : 360 - v)) });
const curve = (from, ...segs) => ({ x: from[0], y: from[1], segs });
// The right half of faf's figure eight: an arm up from the center, a round end, and an arm back down.
const armUp = curve([180, 0], [210, -60, 230, -100, 270, -100]);
const roundEnd = curve([270, -100], [310, -100, 340, -55, 340, 0], [340, 55, 310, 100, 270, 100]);
const armDown = curve([270, 100], [230, 100, 210, 60, 180, 0]);
const crossing = [armUp, armDown, mirror(armUp), mirror(armDown)];
const lessThan = [curve([270, -100], [200, -100, 100, -40, 30, 0], [100, 40, 200, 100, 270, 100])];
const proportional = [...crossing, mirror(roundEnd)];
// paun's left end is a crescent moon: the arms bow out and back to its horns, which an arc joins.
const horn = curve([180, 0], [120, -50, 70, -140, 140, -190]);
const crescent = [horn, curve([140, -190], [-10, -200, -10, 200, 140, 190]), flipStroke(horn)];
const parenthesis = [curve([250, -330], [110, -220, 110, 220, 250, 330])];
const shapes = Object.fromEntries(Object.entries({
'=': [...crossing, roundEnd, mirror(roundEnd)],
'≠': crossing,
'<': lessThan,
'≤': proportional,
'≥': proportional.map(mirror),
'>': lessThan.map(mirror),
// ou: a vertically flipped `A`, with a rounder hoop centered on the baseline and short arms.
'+': [curve([50, -190], [200, -140, 300, -90, 300, 0], [300, 83, 246, 150, 180, 150],
[114, 150, 60, 83, 60, 0], [60, -90, 160, -140, 310, -190])],
// ei: the U that a squeezed `ii` makes, without the strokes that join it to the baseline.
'*': [curve([90, -396], [90, -396, 90, 0, 180, 0], [270, 0, 270, -396, 270, -396])],
'≝': [armUp, roundEnd, armDown, ...crescent],
'↦': [polyline([[60, 120], [300, -120]]), polyline([[180, -120], [300, -120], [300, 0]])],
'(': parenthesis,
')': parenthesis.map(mirror),
}).map(([ch, strokes]) => [ch, { strokes, fills: [] }]));
// The variable mark: a tight circle that feeds into the baseline, ahead of the letter it marks.
shapes['~'] = { strokes: [circle([50, 0], 40), polyline([[90, 0], [180, 0]])], fills: [], width: 180 };
return shapes;
})();
// The shape of a numeral, number mark, or shorthand symbol (a digit pointing down if asked), or undefined.
const symbolShape = (ch, down) => numberSymbol(ch, down) ?? mathSymbolShapes[ch];
// Now that every shape is defined, draw every element with data-glyph="...". Add data-size for scale (in em).
// Add data-marks to draw the listed marks instead of the ones the characters carry, or data-ipa to draw the
// marks an IPA character carries.
document.querySelectorAll('[data-glyph]').forEach(el => {
const listed = el.dataset.marks?.split(' ') ?? (el.dataset.ipa !== undefined ? ipaMarks[el.dataset.ipa] ?? [] : undefined);
const lookup = (ch) => (listed ?? charMarks[ch] ?? []).map(m => glyphMarks[m] ?? fail(`There's no mark named "${m}".`));
el.innerHTML = glyphSVG(el.dataset.glyph, textPathData(el.dataset.glyph, lookup), +el.dataset.size || 2.4);
});
// The SVG half of the script playground (see the Font section). It lives here rather than in the Font
// Builder so the drawings keep working even when the font doesn't. A block keeps its names out of the
// shared global scope.
{
const input = document.getElementById('script-input'), svgOut = document.getElementById('script-svg-out');
const draw = () => {
try {
svgOut.innerHTML = glyphSVG(input.value, textPathData(input.value, (ch) => (charMarks[ch] ?? []).map(m => glyphMarks[m])), 3.5);
} catch (e) {
svgOut.textContent = e.message;
}
};
input.addEventListener('input', draw);
draw();
}
// Numerals and number marks spell out a compact number word (see Numbers).
const numberMarkWords = { ';': 'aih', '₆': 'iaz', '₋': 'iaj' };
tokenParsers.push((s) => {
const [, minus, rest] = s.match(/^(-?)(.*)$/);
const parts = [...rest].map(ch => numeralValue(ch) === undefined ? numberMarkWords[ch] : digitSuff[numeralValue(ch)]);
if (numeralValue(rest[0]) === undefined || parts.includes(undefined)) return;
const tok = makeToken((minus ? 'cr' : 'sl') + parts.join(''));
return tok.type === 'number' ? { ...tok, rawTok: s } : undefined;
});
// A shorthand symbol reads as the words it stands for. Only the first token shows the symbol. Parentheses
// are clause marks that get their slot from the parser (see Nested Clauses).
tokenParsers.push((s) => {
const words = mathSymbols['Character'][s]?.['Papsa Word'];
if ( ! words) return;
if (s === '(' || s === ')') return { rawTok: s, type: 'grammar', clause: s === '(' ? 'open' : 'close', meaning: `${words}, by slot`, contextual: true };
return words.split(' ').map((w, i) => i ? { ...makeToken(w), partOf: s } : { ...makeToken(w), rawTok: s, standsFor: words });
});
// The variable mark makes a pronoun of the letter after it. How it's read depends on context.
tokenParsers.push((s) => {
const ixs = s[0] === '~' ? wordIdxs(s.slice(1)) : [];
if (ixs.length === 1 && isC(ixs[0])) return { rawTok: s, type: 'pronoun', meaning: 'variable', contextual: true };
});
// Each example must parse just like its words. Numbers only need the same value, pronouns are all alike, and
// the order of a token's fields doesn't matter.
addEventListener('DOMContentLoaded', () => {
const shape = (s) => JSON.stringify(parseExpression(tokenize(s)), (k, v) =>
v?.type === 'number' ? { number: v.value, slot: v.slot } : v?.type === 'pronoun' ? { pronoun: v.slot } :
['rawTok', 'standsFor', 'partOf', 'probablyForeign', 'contextual'].includes(k) ? undefined :
v?.constructor === Object ? Object.fromEntries(Object.entries(v).sort()) : v);
for (const [words, row] of symbolExamples['Papsa'])
if (shape(words) !== shape(row['Symbols'])) reportSpecError('Symbol Parsing', `"${row['Symbols']}" doesn't parse like "${words}".`);
});
This page creates its own font, PapsaTasa, which renders ordinary text
as Papsa script. Uppercase letters are never annotated, and can be used when basic letters are
preferred. Lowercase romanized consonants carry the few marks that tell them apart from each
other, so readers can distinguish `k` (`k`) from
`g` (`g`), for example. IPA characters carry the marks
that tell their sound apart from the rest of its letter (see
Minimal Markings), so the font can also set phonetic
transcription. Because Papsa vowels are regular and flexible, and to make them more readable when
squeezed together, `u`, `a`, and `i` are not annotated by
default. (To support names and empower users, some other lowercase vowel characters are annotated,
and a set of combining marks makes any further annotation possible.)
Feel free to Download PapsaTasa.ttf to use for
your own projects.
You can also use this interactive tool to experiment with the rendering engine. PapsaTasa is drawn on the top, and SVG images are drawn below.
To annotate beyond the defaults (a squeezed vowel, a plain `u`, `i`,
or `a`, or a narrower consonant), type one of these combining characters after the
letter. Marks can be stacked, and they shrink along with a squeezed vowel. A full place is its
group's bar followed by one or both tick characters (both for a middle place). The font draws the
ticks to match the bar, so type them right after it. Neutral posture is both hooks together, and a vowel takes the bar and tick characters for its height and backness.
A squeezed `u` is filled in, unless it's followed by variation selector 1 (U+FE00), which keeps it hollow.
| Mark | Character | Unicode name | Example | |:------------|:----------|--------------------------------------|---------| | voiceless | U+0325 | Combining ring below | T̥ | | voiced | U+032C | Combining caron below | T̬ | | implosive | U+1DCD | Combining double circumflex above | P᷍ | | labial | U+0305 | Combining overline | M̅ | | coronal | U+033F | Combining double overline | T̿ | | dorsal | U+0333 | Combining double low line | K̳ | | guttural | U+0332 | Combining low line | H̲ | | frontTick | U+0319 | Combining right tack below | T̙̿ | | backTick | U+0318 | Combining left tack below | K̳̘ | | lateral | U+20DD | Combining enclosing circle | L⃝ | | central | U+20DE | Combining enclosing square | L⃞ | | approximant | U+032E | Combining breve below | L̮ | | tap | U+030D | Combining vertical line above | L̍ | | trill | U+035B | Combining zigzag above | L͛ | | curl | U+0322 | Combining retroflex hook below | C̢ | | domed | U+0321 | Combining palatalized hook below | C̡ | | tense | U+0355 | Combining right arrowhead below | ai͕ | | lax | U+0354 | Combining left arrowhead below | u͔ | | rounded | U+0339 | Combining right half ring below | a̹ | | unrounded | U+031C | Combining left half ring below | u̜ |
// The PapsaTasa font is built here, from the same strokes as the drawings above, so the two can't
// drift apart. It's a TrueType font in which every Papsa character (base letters, romanization, and
// IPA) maps to its letter, and the digits and number marks map to numerals.
//
// How this block fits with the rest of the page (every spec block shares one global scope):
// - Assign the finished glyph table to `scriptGlyphs`, which the runner declares; don't redeclare it.
// The translator uses `scriptGlyphs.cmap` (a Map keyed by code point) once it exists.
// - Don't declare top-level names that the runner declares later (out, inp, tip, annotate, passes,
// and so on). Wrap helpers in a block or a function to keep them out of the global scope.
// - Take the vertical metrics from `scriptExtent` rather than assuming ±396.
// - Report problems, including asynchronous ones, with reportSpecError('Font Builder', message).
// - The SVG drawings in the playground are handled by the Diacritic Drawing block. This block only
// needs to fill #script-font-out and give the download link its href.
//
// Lowercase letters carry the marks `charMarks` gives them, and uppercase letters are bare. Vowels that
// touch are squeezed to half width, as in the drawings. The combining characters in the table above
// attach their marks to whatever letter they follow, squeezed or not, which is how a writer annotates
// the plain vowels u, a, and i. The two tick characters are the exception: they take their drawn form
// from the group bar typed with them (see Place ticks, below).
{
const W = 360, GAP = 180, REACH = 396, PEN = 36, MARK_PEN = 24; // The glyph code's numbers (see Letter Strokes).
// One em is the height of a letter. The script's baseline sits a quarter em above the Latin one, so a
// line of Papsa centers on the lowercase letters of the text around it.
const EM = 2 * REACH, RAISE = EM / 4;
const WORD_CLICKS = new Set(['.', ',', '!']); // Clicks that stand apart like words (see Glyph Drawing).
// The font is built in slices of a few milliseconds, handing the page back in between.
let slice = 0;
const breathe = async () => {
if (performance.now() - slice < 8) return;
await (globalThis.scheduler?.yield?.() ?? new Promise(r => setTimeout(r)));
slice = performance.now();
};
// ── Outlines ──
// A font can't stroke a path, so each stroke is traced as the area a round pen sweeps out: a band along
// each gentle stretch of the path, with a disc wherever the pen starts, stops, or turns sharply. Every
// contour winds clockwise, so TrueType's nonzero fill merges the overlaps, including where a tight curve
// folds a band's inner edge over itself.
const TOL = 1; // How far, in font units (about 1/800 em), an outline may stray from the true stroke.
const disc = ([x, y], r) => Array.from({ length: 16 }, (_, k) => {
const d = k % 2 ? r / Math.cos(Math.PI / 8) : r, a = -k * Math.PI / 8; // Odd points are curve controls.
return [x + d * Math.cos(a), y + d * Math.sin(a), k % 2 === 0];
});
// Points along a path and the left-hand normals of the edges between them -> left edge out, right edge back.
// Interior corners are mitred, which stays within TOL of a round join because the turns here are gentle.
const band = (pts, normals, r) => {
const offsets = pts.map((_, k) => {
const a = normals[k - 1] ?? normals[k], b = normals[k] ?? normals[k - 1], m = r / (1 + a[0] * b[0] + a[1] * b[1]);
return [m * (a[0] + b[0]), m * (a[1] + b[1])];
});
return [...pts.map(([x, y], k) => [x + offsets[k][0], y + offsets[k][1], true]),
...pts.map(([x, y], k) => [x - offsets[k][0], y - offsets[k][1], true]).reverse()];
};
// One stroke ({x, y, segs}, in drawing units), placed by `at` -> contours in font units.
const outline = ({ x, y, segs }, at, pen) => {
const r = pen / 2;
let p = at(x, y);
const path = [p];
for (const s of segs) {
if (s.length === 2) { path.push(p = at(...s)); continue; }
const [a, b, c, d] = [p, at(s[0], s[1]), at(s[2], s[3]), at(s[4], s[5])];
const bend = Math.max(Math.hypot(a[0] - 2*b[0] + c[0], a[1] - 2*b[1] + c[1]), Math.hypot(b[0] - 2*c[0] + d[0], b[1] - 2*c[1] + d[1]));
const n = Math.ceil(Math.sqrt(0.75 * bend / TOL)) || 1; // Enough chords to stay within TOL of the curve.
for (let i = 1; i <= n; i++) {
const t = i / n, u = 1 - t, k = [u*u*u, 3*u*u*t, 3*u*t*t, t*t*t];
path.push([0, 1].map(j => k[0] * a[j] + k[1] * b[j] + k[2] * c[j] + k[3] * d[j]));
}
p = d;
}
const pts = path.filter((q, i) => i === 0 || Math.hypot(q[0] - path[i - 1][0], q[1] - path[i - 1][1]) > 1e-3);
const normals = pts.slice(1).map(([qx, qy], i) => {
const dx = qx - pts[i][0], dy = qy - pts[i][1], l = Math.hypot(dx, dy);
return [-dy / l, dx / l];
});
const maxTurn = 2 * Math.acos(r / (r + TOL)); // Past this, a mitre would poke out beyond the round pen.
const contours = [disc(pts[0], r)];
for (let k = 1, start = 0; k < pts.length; k++) {
const [a, b] = [normals[k - 1], normals[k]];
if (b && Math.abs(Math.atan2(a[0] * b[1] - a[1] * b[0], a[0] * b[0] + a[1] * b[1])) <= maxTurn) continue;
contours.push(band(pts.slice(start, k + 1), normals.slice(start, k), r), disc(pts[k], r));
start = k;
}
return contours;
};
// A filled area (a closed path of straight lines), placed by `at` -> a contour, wound clockwise like the rest.
const area = ({ x, y, segs }, at) => {
const pts = [[x, y], ...segs].map(p => [...at(...p), true]);
const signed = pts.reduce((a, [px, py], k) => a + px * pts[(k + 1) % pts.length][1] - py * pts[(k + 1) % pts.length][0], 0);
return signed > 0 ? pts.reverse() : pts;
};
// ── Binary ──
// `bytes(w => w.u16(...).tag(...))` writes big-endian data. (u16 also writes negative int16s.)
const bytes = (fill) => {
const a = [], w = {
u8: (...v) => { for (const x of v) a.push(x & 255); return w; },
u16: (...v) => { for (const x of v) a.push(x >> 8 & 255, x & 255); return w; },
u32: (...v) => { for (const x of v) a.push(x >>> 24, x >>> 16 & 255, x >>> 8 & 255, x & 255); return w; },
tag: (s) => w.u8(...[...s].map(c => c.charCodeAt(0))),
raw: (b) => { for (const x of b) a.push(x); return w; },
};
fill(w);
return Uint8Array.from(a);
};
const concat = (parts) => {
const out = new Uint8Array(parts.reduce((n, p) => n + p.length, 0));
parts.reduce((at, p) => (out.set(p, at), at + p.length), 0);
return out;
};
const pad4 = (b) => b.length % 4 ? concat([b, new Uint8Array(4 - b.length % 4)]) : b;
// A table followed by the subtables it points to. `head` writes the table, given each subtable's offset.
const linked = (head, kids) => {
let at = head(kids.map(() => 0)).length;
return concat([head(kids.map(k => (at += k.length) - k.length)), ...kids]);
};
const coverage = (gids) => bytes(w => w.u16(1, gids.length, ...gids));
const sorted = (gids) => [...gids].sort((a, b) => a - b);
const box = (pts) => pts.reduce(([a, b, c, d], [x, y]) => [Math.min(a, x), Math.min(b, y), Math.max(c, x), Math.max(d, y)],
[Infinity, Infinity, -Infinity, -Infinity]);
// A GSUB or GPOS table: [tag, lookup indices] for each feature (sorted by tag), all on by default.
const layout = (features, lookups) => {
const langSys = bytes(w => w.u16(0, 0xFFFF, features.length, ...features.keys()));
const script = linked(o => bytes(w => w.u16(o[0], 0)), [langSys]);
const scriptList = linked(o => bytes(w => w.u16(2).tag('DFLT').u16(o[0]).tag('latn').u16(o[0])), [script]);
const featureList = linked(o => bytes(w => { w.u16(features.length); features.forEach(([t], i) => w.tag(t).u16(o[i])); }),
features.map(([, ls]) => bytes(w => w.u16(0, ls.length, ...ls))));
const lookupList = linked(o => bytes(w => w.u16(lookups.length, ...o)), lookups.map(({ type, flag = 0, subtables }) =>
linked(o => bytes(w => w.u16(type, flag, subtables.length, ...o)), subtables)));
return linked(o => bytes(w => w.u16(1, 0, ...o)), [scriptList, featureList, lookupList]);
};
const buildFont = async () => {
slice = performance.now();
// ── Glyphs ──
// Each glyph has contours and an advance. A letter glyph also records its letter, marks, where the
// letter starts (x0), and its horizontal scale (s); a mark glyph records its mark and anchor. A glyph
// with a half-width twin (every vowel and every combining mark) records the twin as `half`.
const glyphs = [], memo = new Map();
const make = (key, build) => {
if ( ! memo.has(key)) {
const g = { contours: [], ...build() };
g.contours = g.contours.map(c => c.map(([x, y, on]) => [Math.round(x), Math.round(y), on]));
g.box = g.contours.length ? box(g.contours.flat()) : [0, 0, 0, 0];
memo.set(key, glyphs.push(g) - 1);
}
return memo.get(key);
};
make('.notdef', () => ({ advance: W }));
const space = make('space', () => ({ advance: GAP }));
// A letter drawn w units wide after a gap, with its marks, placed just as textPathData places it. A
// squeezed letter is filled in unless it's hollow.
const letter = (ltr, marks = [], gap = 0, w = W, hollow = false) => make(['letter', ltr, marks, gap, w, hollow].join('|'), () => {
const s = w / W, at = ([ax, ay]) => (px, py) => [gap + (ax + px) * s, RAISE - ay - py];
const markContours = marks.flatMap(m => {
const a = glyphMarks[m].anchor(ltr);
return a ? glyphMarks[m].strokes.flatMap(st => outline(st, at(a), MARK_PEN)) : [];
});
const fill = w < W && ! hollow && squeezedFills[ltr];
return { letter: ltr, marks, x0: gap, s, advance: gap + w,
contours: [...outline(letterStrokes(ltr), at([0, 0]), PEN), ...(fill ? [area(fill, at([0, 0]))] : []), ...markContours] };
});
const glyphOf = (ch, marks = charMarks[ch] ?? []) => WORD_CLICKS.has(ch) ? letter('!', marks, GAP) : letter(baseLetter(ch), marks);
// Every character that names a Papsa letter, then the other case of each, which is drawn bare.
const cmap = new Map([[0x20, space]]);
const map = (ch, gid) => { if ([...ch].length === 1 && ! cmap.has(ch.codePointAt(0))) cmap.set(ch.codePointAt(0), gid()); };
const keys = (column) => [...column].map(([c]) => c);
const chars = new Set([...Object.keys(charMarks), ...keys(alphabet['Letter']), ...keys(engConsonantRomanization['Romanization']),
...keys(alphabet['Acceptable sounds (IPA)']), ...clickChars]);
for (const ch of chars) { map(ch, () => glyphOf(ch)); await breathe(); }
for (const ch of chars) { for (const c of [ch.toUpperCase(), ch.toLowerCase()]) map(c, () => glyphOf(ch, [])); await breathe(); }
for (const g of [...glyphs]) if (vowelChars.has(g.letter)) { g.half = letter(g.letter, g.marks, 0, W / 2); await breathe(); }
// Variation selector 1 after a letter that fills in when squeezed keeps it hollow. Each such character
// maps to a copy of its glyph whose half-width twin is hollow.
const HOLLOW = 0xFE00, hollows = [];
for (const [cp, gid] of cmap) {
const { letter: ltr, marks, half } = glyphs[gid];
if ( ! half || ! squeezedFills[ltr]) continue;
const twin = letter(ltr, marks, 0, W, true);
glyphs[twin].half = letter(ltr, marks, 0, W / 2, true);
hollows.push([cp, twin]);
}
// Numerals and symbols (see Numerals and Symbols). Each digit comes pointing up or down, and either two letters wide,
// as it is at the end of a row, or one letter wide, with its triangle reaching into the next digit's space.
const symbol = (ch, down, advance) => make(['symbol', ch, down, advance].join('|'), () => {
const { strokes, fills } = symbolShape(ch, down), at = (px, py) => [px, RAISE - py];
return { advance, contours: [...strokes.flatMap(st => outline(st, at, PEN)), ...fills.map(f => area(f, at))] };
});
const digitForms = circledDigits.map((ch, n) => {
const forms = { up: symbol(ch, false, 2 * W), down: symbol(ch, true, 2 * W), upNarrow: symbol(ch, false, W), downNarrow: symbol(ch, true, W) };
map(ch, () => forms.up);
if (n < 10) map(String(n), () => forms.up);
return forms;
});
for (const ch of [...Object.keys(numberMarkShapes), ...Object.keys(mathSymbolShapes)])
map(ch, () => symbol(ch, false, symbolShape(ch).width ?? W));
// Combining marks. Each is drawn about its anchor, offset one letter to the left, so that a mark with
// no letter to attach to still sits over the letter before it.
const combining = [...combiningMarks['Mark']].map(([m, row], cls) => {
if ( ! glyphMarks[m]) fail(`The combining-mark table names "${m}", which isn't a mark.`);
const char = String.fromCodePoint(parseInt(row['Character'].replace(/^U\+/, ''), 16));
const mark = (s) => make(['mark', m, s].join('|'), () => ({ mark: m, cls, s, advance: 0, anchor: [-W * s, RAISE],
contours: glyphMarks[m].strokes.flatMap(st => outline(st, (px, py) => [(px - W) * s, RAISE - py], MARK_PEN)) }));
const full = mark(1);
glyphs[full].half = mark(0.5);
map(char, () => full);
return { m, char, gid: full };
});
// ── Place ticks ──
// The front and back tick characters carry no group of their own, so each gets a drawn form per group:
// long enough to cross both bars of a coronal or dorsal pair, short for the single labial or guttural
// bar, and above or below to match. A tick's form attaches the same way its group's bar does. Ticks give
// a vowel's backness too, so each form also comes at half width for a squeezed vowel.
const gidOf = (m) => combining.find(c => c.m === m)?.gid ?? fail(`The combining-mark table needs "${m}".`);
const withHalf = (gid) => [gid, glyphs[gid].half];
const tickIds = sorted(['frontTick', 'backTick'].flatMap(t => withHalf(gidOf(t))));
const tickVariants = placeGroupList.map(g => {
const bar = gidOf(g.name), cls = glyphs[bar].cls;
const pairs = ['frontTick', 'backTick'].flatMap((t, end) => withHalf(gidOf(t)).map((tick, k) => {
const s = k ? 0.5 : 1;
return [tick, make(['tick', g.name, t, s].join('|'), () => ({ mark: t, cls, s, advance: 0, anchor: [-W * s, RAISE],
contours: outline(tickStroke(g, end), (px, py) => [(px - W) * s, RAISE - py], MARK_PEN) }))];
}));
return { bars: withHalf(bar), pairs: pairs.sort((a, b) => a[0] - b[0]) };
});
await breathe();
// ── Layout ──
const ids = (pred) => glyphs.flatMap((g, i) => pred(g) ? [i] : []);
const twins = ids(g => g.half !== undefined);
const fullVowels = twins.filter(i => ! glyphs[i].mark), fullMarks = twins.filter(i => glyphs[i].mark);
const halfVowels = sorted(fullVowels.map(i => glyphs[i].half)), halfMarks = sorted(fullMarks.map(i => glyphs[i].half));
const allVowels = sorted([...fullVowels, ...halfVowels]);
// Lookup 0 swaps a glyph for its half-width twin, and the contextual lookups decide when. A vowel is
// squeezed when the next or previous letter is a vowel (looking past combining marks), and a mark is
// squeezed when it follows a squeezed vowel or a squeezed mark. `chain` takes the lookup to apply, then
// coverages for the backtrack, input, and lookahead.
const chain = (lookup, ...sets) => linked(o => bytes(w => {
let k = 0;
w.u16(3);
for (const set of sets) w.u16(set.length, ...set.map(() => o[k++]));
w.u16(1, 0, lookup);
}), sets.flat().map(coverage));
// Lookups 3 and up give the tick characters one group's drawn form each, and the last lookup decides
// which group applies: the bar (or tick) just before, or the bar just after. Text shaping sorts marks
// below ahead of marks above, so a tick can end up in front of a bar above.
// `swap` takes [glyph, replacement] pairs sorted by glyph.
const swap = (pairs) => ({ type: 1, subtables: [linked(o => bytes(w => w.u16(2, o[0], pairs.length, ...pairs.map(p => p[1]))), [coverage(pairs.map(p => p[0]))])] });
const tickLookups = tickVariants.map(({ pairs }) => swap(pairs));
const pickTicks = 3 + tickLookups.length;
// The last four lookups lay out digits. Every digit starts pointing up and two letters wide. Left to right,
// a digit right after one pointing up turns to point down, and any digit followed by another narrows.
const forms = (...keys) => sorted(digitForms.flatMap(f => keys.map(k => f[k])));
const pairsOf = (from, to) => digitForms.flatMap(f => from.map((k, i) => [f[k], f[to[i]]])).sort((a, b) => a[0] - b[0]);
const turn = pickTicks + 1, narrow = pickTicks + 2, digitLookups = [pickTicks + 3, pickTicks + 4];
const GSUB = layout([['calt', [1, 2, pickTicks, ...digitLookups]], ['rclt', [1, 2, pickTicks, ...digitLookups]]], [
{ type: 1, subtables: [linked(o => bytes(w => w.u16(2, o[0], twins.length, ...twins.map(i => glyphs[i].half))), [coverage(twins)])] },
{ type: 6, flag: 8 /* ignore marks */, subtables: [chain(0, [], [fullVowels], [allVowels]), chain(0, [allVowels], [fullVowels], [])] },
{ type: 6, subtables: [chain(0, [sorted([...halfVowels, ...halfMarks])], [fullMarks], [])] },
...tickLookups,
{ type: 6, subtables: tickVariants.flatMap(({ bars, pairs }, k) => [
chain(3 + k, [sorted([...bars, ...pairs.map(p => p[1])])], [tickIds], []),
chain(3 + k, [], [tickIds], [sorted(bars)]),
chain(3 + k, [], [tickIds], [tickIds, sorted(bars)]),
]) },
swap(pairsOf(['up', 'upNarrow'], ['down', 'downNarrow'])),
swap(pairsOf(['up', 'down'], ['upNarrow', 'downNarrow'])),
{ type: 6, subtables: [chain(turn, [forms('up', 'upNarrow')], [forms('up')], [])] },
{ type: 6, subtables: [chain(narrow, [], [forms('up', 'down')], [forms('up', 'down')])] },
]);
// Each combining mark attaches where that mark sits on the letter, squeezed or not. Letters with no
// place for a mark (a lateral bar on a K, say) leave it unattached.
const anchor = (a) => bytes(w => w.u16(1, ...a.map(Math.round)));
const marks = ids(g => g.mark), bases = ids(g => g.letter);
const shared = new Map(), anchors = [];
const slot = (a) => { const k = a.map(Math.round).join(); if ( ! shared.has(k)) shared.set(k, anchors.push(anchor(a))); return shared.get(k); };
const records = bases.map(i => combining.map(({ m }) => {
const g = glyphs[i], a = glyphMarks[m].anchor(g.letter);
return a ? slot([g.x0 + a[0] * g.s, RAISE - a[1]]) : 0; // 1-based, so 0 can mean no anchor
}));
const markArray = linked(o => bytes(w => { w.u16(marks.length); marks.forEach((i, k) => w.u16(glyphs[i].cls, o[k])); }),
marks.map(i => anchor(glyphs[i].anchor)));
const baseArray = linked(o => bytes(w => { w.u16(bases.length); for (const r of records) w.u16(...r.map(k => k && o[k - 1])); }), anchors);
const GPOS = layout([['mark', [0]]], [{ type: 4, subtables: [linked(o => bytes(w => w.u16(1, o[0], o[1], combining.length, o[2], o[3])),
[coverage(marks), coverage(bases), markArray, baseArray])] }]);
const GDEF = bytes(w => w.u16(1, 0, 12, 0, 0, 0, 1, 0, glyphs.length, ...glyphs.map(g => g.mark ? 3 : g.letter ? 1 : 0)));
// ── Outlines and metrics ──
// Coordinates are deltas from the point before: one byte if they fit, and nothing at all if zero.
const encode = ({ contours, box: b }) => {
if ( ! contours.length) return new Uint8Array(0);
const flags = [], xs = [], ys = [];
const delta = (d, out, short, same) => {
if (d === 0) return same;
if (Math.abs(d) < 256) { out.push(Math.abs(d)); return short | (d > 0 ? same : 0); }
out.push(d >> 8 & 255, d & 255);
return 0;
};
let x0 = 0, y0 = 0, end = -1;
contours.flat().forEach(([x, y, on], i) => {
flags.push((on ? 1 : 0) | (i ? 0 : 0x40 /* contours overlap */) | delta(x - x0, xs, 0x02, 0x10) | delta(y - y0, ys, 0x04, 0x20));
[x0, y0] = [x, y];
});
return bytes(w => w.u16(contours.length, ...b, ...contours.map(c => end += c.length), 0).raw(flags).raw(xs).raw(ys));
};
const outlines = glyphs.map(g => pad4(encode(g)));
const glyf = concat(outlines);
const loca = bytes(w => { let at = 0; for (const o of outlines) { w.u32(at); at += o.length; } w.u32(at); });
const hmtx = bytes(w => { for (const g of glyphs) w.u16(g.advance, g.box[0]); });
const inked = glyphs.filter(g => g.contours.length);
const [xMin, yMin, xMax, yMax] = box(inked.flatMap(g => [g.box.slice(0, 2), g.box.slice(2)]));
const ascender = Math.round(RAISE - scriptExtent.top), descender = Math.round(RAISE - scriptExtent.bottom);
const stamp = Date.UTC(2026, 0, 1) / 1000 + 2082844800; // A fixed date (in seconds since 1904), so every build is identical.
const top = RAISE + REACH; // The top of every letter, which serves as both x-height and cap height.
const head = bytes(w => w.u32(0x00010000, 0x00010000, 0, 0x5F0F3CF5).u16(0x0009, EM).u32(0, stamp, 0, stamp)
.u16(xMin, yMin, xMax, yMax, 0, 8, 2, 1 /* long loca */, 0));
const hhea = bytes(w => w.u32(0x00010000).u16(ascender, descender, 0, Math.max(...glyphs.map(g => g.advance)),
Math.min(...inked.map(g => g.box[0])), Math.min(...inked.map(g => g.advance - g.box[2])), xMax, 1, 0, 0, 0, 0, 0, 0, 0, glyphs.length));
const maxp = bytes(w => w.u32(0x00010000).u16(glyphs.length, Math.max(...glyphs.map(g => g.contours.flat().length)),
Math.max(...glyphs.map(g => g.contours.length)), 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0));
const post = bytes(w => w.u32(0x00030000, 0).u16(RAISE - REACH - PEN, PEN).u32(0, 0, 0, 0, 0));
const cps = sorted(cmap.keys());
const advances = glyphs.map(g => g.advance).filter(Boolean);
const OS2 = bytes(w => w.u16(4, Math.round(advances.reduce((a, b) => a + b) / advances.length), 400, 5, 0)
.u16(...Array(11).fill(0)).u8(...Array(10).fill(0)).u32(0, 0, 0, 0).tag('NONE')
.u16(0x00C0 /* regular, use typo metrics */, cps[0], cps.at(-1), ascender, descender, 0, Math.max(ascender, yMax), Math.max(-descender, -yMin))
.u32(0, 0).u16(top, top, 0, 0x20, 3));
const names = ['PapsaTasa', 'Regular', 'PapsaTasa-Regular', 'PapsaTasa', 'Version 1.000', 'PapsaTasa']; // IDs 1 to 6
const strings = names.map(s => bytes(w => w.u16(...[...s].map(c => c.charCodeAt(0)))));
const name = bytes(w => {
w.u16(0, names.length, 6 + 12 * names.length);
strings.reduce((at, s, i) => (w.u16(3, 1, 0x409, i + 1, s.length, at), at + s.length), 0);
strings.forEach(s => w.raw(s));
});
// Runs of code points whose glyph ids rise in step share a segment.
const segs = [];
for (const cp of cps) {
const s = segs.at(-1), delta = cmap.get(cp) - cp;
if (s && cp === s.end + 1 && delta === s.delta) s.end = cp;
else segs.push({ start: cp, end: cp, delta });
}
segs.push({ start: 0xFFFF, end: 0xFFFF, delta: 1 });
const n = segs.length, range = 2 << Math.floor(Math.log2(n));
const format4 = bytes(w => w.u16(4, 16 + 8 * n, 0, 2 * n, range, Math.log2(range / 2), 2 * n - range)
.u16(...segs.map(s => s.end), 0, ...segs.map(s => s.start), ...segs.map(s => s.delta), ...segs.map(() => 0)));
// The variation sequences (see Glyphs), with code points as three bytes.
const u24 = (v) => [v >> 16, v >> 8, v];
const format14 = bytes(w => {
w.u16(14).u32(25 + 5 * hollows.length, 1).u8(...u24(HOLLOW)).u32(0, 21, hollows.length);
for (const [cp, gid] of hollows.sort((a, b) => a[0] - b[0])) w.u8(...u24(cp)).u16(gid);
});
const cmapTable = concat([bytes(w => w.u16(0, 3, 0, 3).u32(28).u16(0, 5).u32(28 + format4.length).u16(3, 1).u32(28)), format4, format14]);
// ── The file ──
// The table directory, then the tables, each padded to four bytes and checksummed.
const tables = { GDEF, GPOS, GSUB, 'OS/2': OS2, cmap: cmapTable, glyf, head, hhea, hmtx, loca, maxp, name, post };
const tags = Object.keys(tables).sort(), padded = tags.map(t => pad4(tables[t]));
const checksum = (b) => {
const v = new DataView(b.buffer, b.byteOffset, b.byteLength);
let s = 0;
for (let i = 0; i < b.length; i += 4) s = s + v.getUint32(i) >>> 0;
return s;
};
const dirRange = 16 << Math.floor(Math.log2(tags.length));
const offsets = padded.reduce((acc, p) => [...acc, acc.at(-1) + p.length], [12 + 16 * tags.length]);
const directory = bytes(w => {
w.u32(0x00010000).u16(tags.length, dirRange, Math.log2(dirRange / 16), 16 * tags.length - dirRange);
tags.forEach((t, i) => w.tag(t).u32(checksum(padded[i]), offsets[i], tables[t].length));
});
const font = concat([directory, ...padded]);
new DataView(font.buffer).setUint32(offsets[tags.indexOf('head')] + 8, 0xB1B0AFBA - checksum(font) >>> 0);
return { glyphs, cmap, combining, font };
};
// Renders a sample both ways and compares the ink, so the font can't drift from the drawings. The sample
// runs twice, and the second time it spells its marks with combining characters, which must land exactly
// where the built-in marks do. Browsers that can't turn off hinting for canvas text (which nudges letters
// by whole pixels) skip the check.
const compareWithSVG = (combining) => {
if ( ! ('textRendering' in (globalThis.CanvasRenderingContext2D?.prototype ?? {}))) return;
// Every digit appears pointing both ways, since the second run is shifted by one.
const digits = circledDigits.join('');
const sample = "neim kud. pif xarc sou mauk a'apaun θŋ lr vaiuz NIIM KUT! seiou ɪʊəɛæʌɑ hx ɖɓʂɕʃ ʎɾʀɹ ɦɲqð ǁǂʔ " +
`${digits} ⓪${digits} 113 -③;⑤③⑱₋㉕ ②₆㉚ = ≠ <≤≥> +* ≝ ( ~N ↦ ~n ) ②)`;
const charOf = new Map(combining.map(({ m, char }) => [m, char]));
// A composite mark (a full place, or neutral posture) is spelled with the combining marks that make it
// up. Click characters are left as they are, since the bare click ("!") would stand apart as a word.
const spell = (m) => (markSpelling[m] ?? [m]).map(x => charOf.get(x));
const spelled = [...sample].map(ch => {
const chars = (charMarks[ch] ?? []).flatMap(spell);
return chars.length && chars.every(Boolean) && baseLetter(ch) !== '!' ? baseLetter(ch) + chars.join('') : ch;
}).join('');
const { letterPath, diacriticPath, fillPath, width } = textPathData(sample + ' ' + sample, ch => (charMarks[ch] ?? []).map(m => glyphMarks[m]));
const scale = 1 / 6, pad = 12, mid = pad + Math.ceil(-scriptExtent.top * scale); // 1/6 keeps every advance a whole pixel.
const w = Math.ceil(width * scale) + 2 * pad, h = mid + Math.ceil(scriptExtent.bottom * scale) + pad;
const ink = (draw) => {
const ctx = Object.assign(document.createElement('canvas'), { width: w, height: h }).getContext('2d');
draw(ctx);
return ctx.getImageData(0, 0, w, h).data;
};
const viaFont = ink(ctx => {
Object.assign(ctx, { font: `${EM * scale}px PapsaTasa`, textRendering: 'geometricPrecision' });
ctx.fillText(sample + ' ' + spelled, pad, mid + RAISE * scale);
});
const viaSVG = ink(ctx => {
ctx.setTransform(scale, 0, 0, scale, pad, mid);
ctx.lineCap = ctx.lineJoin = 'round';
for (const [d, lineWidth] of [[letterPath, PEN], [diacriticPath, MARK_PEN]]) { ctx.lineWidth = lineWidth; ctx.stroke(new Path2D(d)); }
ctx.fill(new Path2D(fillPath));
});
let differ = 0, inked = 0;
for (let i = 3; i < viaFont.length; i += 4) {
differ += Math.abs(viaFont[i] - viaSVG[i]) > 160;
inked += Math.max(viaFont[i], viaSVG[i]) > 127;
}
if (differ > inked / 2000) reportSpecError('Font Builder', `The font and the SVG drawings disagree on ${differ} of ${inked} inked pixels.`);
};
(async () => {
await new Promise(r => setTimeout(r)); // Let the rest of the page finish first.
const { glyphs, cmap, combining, font } = await buildFont();
const face = new FontFace('PapsaTasa', font);
await face.load().catch(e => fail(`The browser rejected the font (${e.message}).`));
document.fonts.add(face);
scriptGlyphs = { glyphs, cmap };
const url = URL.createObjectURL(new Blob([font], { type: 'font/ttf' }));
document.querySelectorAll('a[data-font-download]').forEach(a => a.href = url);
await new Promise(r => (globalThis.requestIdleCallback ?? setTimeout)(r));
compareWithSVG(combining);
})().catch(e => reportSpecError('Font Builder', e.message ?? e));
// The playground: what's typed, set in the font.
const input = document.getElementById('script-input'), shown = document.getElementById('script-font-out');
const show = () => { shown.textContent = input.value; };
input.addEventListener('input', show);
show();
}
While Papsa's root words are the heart of its vocabulary, to speak with any normal amount of precision and clarity, one must use compound words that are structurally composed of one or more root words fused together. As a basic rule of thumb, a compound is like taking a root word and augmenting it with another root, then taking that word and augmenting with another root, and so on. In other words, compounds are most semantically similar to the leftmost root, and are left-associating in how they're composed. But of course, this is just a heuristic. The meaning of a compound is often divergent from what the augmented roots would imply, usually by being more specific, but also sometimes in being just plain different.
When forming a compound by adding a root word on to the right of an existing word, start by "deleting the space" and then follow these general rules for making sure the resulting mid-word consonant cluster is valid:
As an example, let's combine `sih` and `xih` to make a word meaning
something like "unordered list" (a bit of an oxymoron, but hey, it's a concept in English).
First, we combine, and get "sihxih". The middle letters are both `H`, so we collapse
that cluster down to simply "x", and get `sixih`. Now suppose we want to compound
that again to invent a word for "an unordered list that is divided into sections" by adding
`fic` to the end. "sixihfic" doesn't have any double-letters, but we need to get rid
of the internal h. The final result is `sixific`. But now what if someone else has
already combined `sihif` ("steps") with `fic` to create
`sihific` ("divided steps"?). Despite the slight difference in pronunciation, this
would have the same (non-annotated) spelling, so we'd be forced to change our word to something
like `sixiflic`.
In practice, namespace collisions are very rare, which is why the example is somewhat contrived. As long as neologisms are reserved for concepts with real generality and utility, things stay fairly natural.
English usually doesn't have adverbs that cover specific concepts. When absent, consider the adverb use to basically be "like-an-X" for whatever the noun form of the word is. Likewise, a compound with no verb gloss still works as a verb, taking its sense from its base; it's just too specific for a clean English gloss.
| Word | Noun | Verb (Transitive) | Notes | Roots | Adjective | Verb (Intransitive) | Adverb |
|------|------|-------------------|-------|-------|-----------|---------------------|--------|
| `natik` | toy, game | to play a game with, to use a toy to pretend, to play toys with | | `nat`, `tik` | - | to play with a toy, to play a game | - |
| `natikmaz` | ball, sport ball | to play a ball game with | | `natik`, `maz` | - | to play a ball game | - |
| `mumum` | water, H2O, liquid water | to hydrate | | `mum`, `mum` | hydrated | to be water | - |
| `mumvuf` | ocean, sea, great lake | to be the ocean near | | `mum`, `vuf` | tidal, oceanic, marine | to be an ocean | - |
| `mazmum` | bubble | to bubble up through | | `maz`, `mum` | bubbling, bubbly | to bubble, to bubble up | bubblingly |
| `vufmum` | coastland, island, peninsula, beach, shore, swamp, wetland, marsh | - | | `vuf`, `mum` | - | - | - |
| `vufmumtait` | continent | to be the continent containing | | `vufmum`, `tait` | continental | to be a continent | - |
| `dutvuf` | mountain, peak | to challenge a (perhaps metaphorical) mountain-climber, to loom over | | `dut`, `vuf` | mountainous | to be a mountain, to loom | - |
| `vufdut` | mountain range, mountains | to be the mountainous region near | | `vuf`, `dut` | mountainous | to be mountainous | - |
| `mumcuk` | ice, frost, snow | to freeze | | `mum`, `cuk` | icy, frosty, snowy | to freeze, to ice over | icily |
| `mumcuktait` | glacier, iceberg | to encase (in ice) | | `mumcuk`, `tait` | glacial | to be a glacier | glacially |
| `mumkuh` | river, stream, creek, brook, flood | to wash, to flood, to rinse | | `mum`, `kuh` | fluvial, riparian, riverine, flooded, washed-up, washed-away | to flow | - |
| `maukuh` | bird, avian | to fly like a bird in response to | See also: `kuxuh` and `tikuxuh`. | `mauk`, `xuh` | avian, birdlike, feathered | to behave like a bird, to fly | - |
| `maukmum` | fish, shellfish, whale, dolphin, shark, plankton, jellyfish, swimmer | to swim in | This is one of the few `mauk` compounds to be regularly used as a verb. When used transitively, the object is the body of water where the creature swims. Boats use `kumum` or other words. | `mauk`, `mum` | aquatic, marine, fishy | to behave like a marine animal, to swim | - |
| `maukcoum` | insect, arthropod, arachnid, worm, bug | to swarm, to crawl over | | `mauk`, `coum` | insectile, swarming, skittering, crawling | to swarm, to squirm, to crawl | - |
| `coumas` | caterpillar, worm, maggot, larva, grub | to inch along | A stage of life as much as a kind of creature, since many `coumas` grow up into something else. | `coum`, `has` | wormy, larval, wriggling | to wriggle, to inch along | - |
| `xufik` | rainbow, spectrum | to (with many colors) hang above | | `xuh`, `fik` | spectral | to be a rainbow | - |
| `xujuc` | land, underworld, lowlands, ocean floor, gravity well | to be below, to be beneath | | `xuh`, `juc` | low, fallen, sunken, downward | to drop, to sink, to fall off, to descend | - |
| `hascap` | blade, sword, saber | to slash, to hack | | `has`, `cap` | bladed | to slash, to hack, to fence | - |
| `hastuah` | spear, lance, pike, javelin, skewer, stake | to spear, to impale, to skewer, to lance | | `has`, `tuah` | impaled, skewered | to thrust | - |
| `hasuh` | tower, spire, pole | to rise above, to tower over | | `has`, `xuh` | towering | to rise up, to point upwards, to tower | - |
| `kuxuh` | launch-pad, spring, leg, rocket, wing | to climb, to jump onto | | `kuh`, `xuh` | rising, floating, springy, bouncy, ascending | to jump, to take off, to climb, to rise, to ascend, to lift off, to float up | ascendingly |
| `tautaz` | table, counter, surface, desk, bench, platform, altar | to be a table that supports, to be a table that shows | | `taut`, `taz` | tabular | to be a table | - |
| `tautun` | door, gate, portal, entrance, exit, opening, passage | to be a portal or door that allows access to | | `taut`, `hun` | doored, gated | to be a door | - |
| `punmain` | greeter, introducer, newcomer-to-conversation | to greet, to hail | | `pun`, `main` | greeting, initiating | to greet, to say hello | initially |
| `pundut` | ender-of-conversation | to say goodbye to, to wrap up with | | `pun`, `dut` | closing, final, concluding | to say goodbye, to end a conversation, to sign off, to hang up, to disconnect, to conclude | terminally |
| `kuvin` | newcomer, arriver, incoming traveler, approacher | to come to, to approach, to head toward, to arrive at | Travel aimed at "here," toward the speaker or some obviously local spot. Where `vin` is reaching a place (or being there), `kuvin` puts the weight on the journey, so it can describe something still on its way. Contrast with `vinjuc`. | `kuh`, `vin` | incoming, approaching, arriving, oncoming | to come, to approach, to arrive, to come here | toward here, hither, inbound |
| `figuk` | red | to be as red as | | `fik`, `guk` | red | to be red | - |
| `figuih` | yellow | to be as yellow as | | `fik`, `guih` | yellow | to be yellow | - |
| `fikbup` | green | to be as green as | | `fik`, `bup` | green | to be green | - |
| `fikuh` | cyan | to be as cyan as | | `fik`, `xuh` | cyan | to be cyan | - |
| `fikmum` | blue | to be as blue as | | `fik`, `mum` | blue | to be blue | - |
| `fiknuŋ` | purple, magenta | to be as purple as | | `fik`, `nuŋ` | purple, magenta | to be purple | - |
| `fikcuc` | blackness, darkness | to be as dark as | | `fik`, `juc` | black, dark | to be dark | darkly |
| `fikzuip` | white, lightness, brightness | to be as light as | | `fik`, `zuip` | white, bright, light | to be light | brightly |
| `vufzuip` | universe, world | to be the universe of | | `vuf`, `zuip` | universal | to be a universe | universally |
| `vufusoun` | galactic cluster | to be the (home) galactic cluster of | | `vuf`, `xuh`, `soun` | - | to be a galactic cluster | - |
| `vufusaum` | galaxy | to be the (home) galaxy of | | `vuf`, `xuh`, `saum` | galactic | to be a galaxy | - |
| `guixuh` | star | to be the nearest star to | | `guih`, `xuh` | stellar | to be a star | - |
| `vufusuk` | star | to be the nearest star to | | `vuf`, `xuh`, `suk` | stellar | to be a star | - |
| `vufuh` | planet, world | to be the planet of | | `vuf`, `xuh` | planetary | to be a planet | - |
| `vufusat` | planet, world | to be the planet of | | `vuf`, `xuh`, `sat` | planetary | to be a planet | - |
| `vufudut` | moon, satellite | to be the moon of | | `vuf`, `xuh`, `dut` | lunar | to be a moon | - |
| `vufuseip` | moon, satellite | to be the moon of | | `vuf`, `xuh`, `seip` | lunar | to be a moon | - |
| `mazguih` | day | to be the daily routine of | | `maz`, `guih` | daily | to (with the changing light) repeat | daily |
| `mazguiguih` | daytime, day, daylight | to (in the daytime) reveal | | `mazguih`, `guih` | diurnal | to be daytime | - |
| `mazguijuc` | night, nighttime | to (in the night) conceal | | `mazguih`, `juc` | nocturnal, nightly | to be night | nightly |
| `xufaf` | weather | to be weather affecting | | `xuh`, `faf` | - | to be weather | - |
| `xuguih` | sunshine, sun | to be sunshine upon | | `xuh`, `guih` | sunny | to be sunny | sunnily |
| `xumum` | rain, precipitation, snow, sleet, hail | to rain on, to snow on | While this means precipitation in general, it implies rain by default, since snow is often distinguished as `xucuk`. | `xuh`, `mum` | rainy, stormy | to rain, to snow, to sleet, to hail, to precipitate | - |
| `xucuk` | snow, sleet, hail | to snow on, to be snow on | | `xuh`, `cuk` | snowy | to snow, to sleet, to hail | - |
| `huvin` | fog | to (as fog) envelop | | `huf`, `vin` | foggy | to be foggy | - |
| `xukim` | wind | to blow on | | `xuh`, `kim` | windy | to blow | - |
| `xukimait` | breeze | to blow gently on | | `xukim`, `hait` | breezy | to blow gently | - |
| `naunuŋ` | value, values, worth, treasure | to be valued by, to matter to, to be treasured by | Where `naun` is being good for someone whether or not they recognize it, `naunuŋ` is goodness as it registers in a mind — what an agent actually cares about. The two come apart whenever someone treasures what harms them, or is helped by something they never notice. Compare `neimnaun`, which is the feeling of things going well, rather than the valuing itself. | `naun`, `nuŋ` | valued, precious, cherished, treasured, meaningful | to matter, to be valued | meaningfully, dearly |
| `miniŋ` | curiosity, inquisitiveness, wonder, open question | to be curious about, to wonder about, to want to know, to inquire into | The drive to know, described as a fact about what a mind is seeking rather than as a feeling — a search process can have `miniŋ` without there being anything it is like to be it. Compare `neimnuŋ`, which is fascination as an experience. | `min`, `niŋ` | curious, inquisitive, questioning, unresolved | to be curious, to wonder | curiously, inquisitively |
| `kanuŋ` | teaching, education, schooling, instruction | to teach, to inform, to educate, to raise (a mind) | | `kan`, `nuŋ` | educational, instructive, formative | to teach, to inform | instructively |
| `mufniŋ` | student, learner, pupil | to learn, to study, to come to understand | | `muf`, `niŋ` | studious, learned, educated | to learn, to study | studiously |
| `tinuŋ` | soul, essential self, innermost self, character | to see into the soul of, to know (someone) deeply | The condensed core of a mind — whatever would remain if the incidentals were stripped away. `tinuŋ` carries no commitment to an afterlife, to an immaterial substance, or even to there being exactly one per mind; it names the essence and leaves the metaphysics to the speaker. | `tiŋ`, `nuŋ` | soulful, innermost, essential, deep | to soul-search, to look inward | deeply, essentially |
| `funuŋ` | culture, upbringing, heritage, tradition, formative background | to raise, to form the mind of, to be the upbringing of | The mind-shaping someone came out of, especially in childhood — the content, not the group that carries it. Any mind with an upbringing has a `funuŋ`, so the word extends past human societies without strain. | `fun`, `nuŋ` | cultural, traditional, ancestral, formative | to raise, to form minds | culturally, traditionally |
| `paunaun` | hint, clue, tip, pointer, cue | to hint at, to allude to, to point helpfully toward, to tip off about | A sign offered for the benefit of whoever reads it. A `paunaun` is characteristically incomplete — it points toward the answer instead of stating it — but this implies nothing about concealment; the withholding may be kindness, brevity, or pedagogy. | `paun`, `naun` | suggestive, indicative, telling, leading | to hint | suggestively |
| `neimin` | yearning, longing, wistfulness, homesickness | to yearn for, to long for, to pine for, to miss | Where `min` is the bare fact of wanting something, `neimin` is wanting as it is felt — the ache rather than the gap. Belongs to the `neim` emotion series alongside `neimnaun` and `neimcun`. | `neim`, `min` | yearning, longing, wistful, homesick | to yearn, to long, to pine | yearningly, wistfully |
| `mamin` | improvement, progress, change for the better | to improve into, to grow into | The first vowel is "a", so the noun is the change itself rather than the thing improved. Unlike `kan`, which puts an enhancer in the subject slot, `mamin` says only that things moved toward what was wanted; a recovery, a thaw, or a lucky turn all qualify. | `mam`, `min` | improving, better, progressive | to improve, to get better, to change for the better | progressively |
| `kimam` | transformer, converter | to transform, to alter, to convert, to adapt | | `kim`, `mam` | transformative | to transform, to alter, to convert | transformatively |
| `kimain` | stirring, onset of motion, first movement | to stir, to rouse | The moment motion begins, rather than the state that follows. Distinct from `kimcukcuc`, which is wakefulness as the negation of `kimcuk` (sleep): a stalled cart can `kimain` without having slept, and a still, watchful animal is `kimcukcuc` with no `kimain` in sight. Works reflexively, such that when an object is elided it is often (but necessarily) assumed that the person is rousing themself. | `kim`, `main` | stirring, rousing, newly active | to start moving, to stir, to come to life | - |
| `tumain` | inventor, originator, innovator, pioneer | to invent, to originate, to pioneer, to make for the first time | The first vowel is "u", so the noun is the maker rather than the invention. `tum` alone is anyone who makes; `tumain` is reserved for making a thing of a kind that did not exist yet. | `tum`, `main` | inventive, innovative, original, pioneering | to invent, to innovate | inventively |
| `pimain` | new parent, new caretaker, adoptive parent, foster parent, new guardian, new mentor | to be newly taken care of by, to be newly raised by | Names the caretaker's side of a new care relationship. `pim` puts the ward in the subject slot, so the verb reads "is newly cared for by" while the noun is the one doing the caring. Adoption, fostering, a new mentor and a new nurse all qualify alongside a first-time birth parent. Compare `papmain`, which names the child instead. | `pim`, `main` | newly parental, adoptive, fostering | to be newly cared for | - |
| `feimain` | fusion, synthesis, merger, new whole | to fuse, to merge into something new, to synthesize | Combining that yields something new, as against `feim`, where the parts survive the mixing. A salad is `feim`; an alloy, a creole, or a merged company is `feimain`. | `feim`, `main` | fused, merged, synthesized, hybrid | to be a source of fusion | - |
| `nikan` | optimum, best case, ideal, ceiling | to optimize for, to pursue the optimum of, to maximize | Always optimal relative to some `nik` — a goal — never in the abstract. Calling a thing `nikan` invites the question "for what?", and speakers usually supply it. | `nik`, `kan` | optimal, ideal, best, peak | to optimize | optimally, ideally |
| `pauneim` | emotional expression, affect, tone, body language | to express feeling about | The outward sign of an inner state, through whatever channel a speaker has — face, voice, posture, color, exhaust plume. Carries no claim of sincerity: a `pauneim` can be performed, mistaken, or read wrongly. | `paun`, `neim` | expressive, emotive, demonstrative | to emote, to show how one feels | expressively |
| `nikud` | motivation, inspiration, drive, incentive, reason for acting | to be motivatated by, to be driven toward a goal, to be spurred by, to be inspired by | The push toward a `nik`, from inside or out: appetite, duty, a paycheck and a threat are all `nikud`. Where `kud` is bare force, `nikud` is force with a direction. | `nik`, `kud` | motivating, motivated, driven | to be motivated | - |
| `tigab` | net, mesh, web, snare | to net, to catch in a net, to ensnare deliberately | `gab` covers webs and traps of every origin; `tigab` is the made kind, a `tik` built to entangle. The mesh sense extends readily to networks and grids, where what gets caught is traffic rather than fish. | `tik`, `gab` | netted, meshed, woven | to use a net | - |
| `gapif` | bait, lure, honeypot | to bait, to lure, to entice | | `gap`, `pif` | enticing, tempting | to bait, to lure | enticingly, temptingly |
| `feimuf` | froth, foam, whipped cream | to whip, to froth, to aerate, to fluff | | `feim`, `muf` | whipped, frothy, aerated | to froth, to whip | frothily |
| `bikan` | vitamin, supplement, fortified food, micronutrient, superfood | to be fortified by | Food that contains extra nutrients that go above and beyond normal food. | `bik`, `kan` | fortifying, enriched | to supplement, to take supplements | - |
| `dupaun` | alarm, crier, alert, siren | to alarm, to alert, to warn of danger | Something or someone who cries out in the face of danger to protect people. | `dup`, `paun` | alarmed, on alert, alarming | to alarm, to raise the alarm | alarmingly |
| `mukait` | microbe, microorganism, cell, bacteria, virus, protozoa | - | In contrast with `coum`, this has no connotation of disease or parasitism. | `muk`, `hait` | microbial, cellular, miroscopic | - | - |
| `futik` | pouch, toolbox, pocket, pack, bag, backpack, satchel, sheath, quiver, holster, purse | to pack, to stow, to contain tools | A container for tools and useful artifacts. | `fut`, `tik` | pocketable, portable | to pack | - |
| `tikfut` | clamp, vise, tongs, pliers, tweezers, forceps, clip, chopsticks, clothespin | to clamp, to grip, to clasp | A tool whose job is to grab and hold things. | `tik`, `fut` | gripping, clamping, grasping | to clamp, to grip | graspingly |
| `dupuf` | padding, cushion, packing, wadding | to pad, to cushion, to buffer | | `dup`, `huf` | padded, cushioned | to pad, to cushion | - |
| `minuif` | enough | to be satisfied by, to be content with | The edge-kind-of-desire is something that is wanted, but only up to a point. Can also be used to talk about diminishing returns. | `min`, `nuif` | sufficient, adequate, satisfactory, good enough | to be satisfied, to be content, to have enough | sufficiently |
| `figab` | stain, dye, tattoo, ink | to dye, to tattoo, to mark permanently | Color that has been caught and won't come out. | `fik`, `gab` | dyed, stained | to dye | - |
| `papuf` | huddle, cuddle pile, hug, embrace | to hug, to nuzzle, to snuggle, to cuddle, to huddle with, to gather close, to nestle among | More about the social act of being soft and welcoming than any particular action or physical configuration. Penguins packed together in a storm. A baby squirrel in a nest. Grandparents offering a cup of cocoa after a long journey. | `pap`, `huf` | cozy, snug, homey, hygge, close-knit | to hug, to snuggle, to cuddle, to huddle | snugly, cozily |
| `faipif` | seasoning, spice, condiment, flavoring, additive, dopant, admixture | to season, to spice, to flavor, to dose, to lace | | `faip`, `pif` | seasoned, spiced, flavored, laced | to season, to spice | - |
| `nuivuf` | borderland, frontier, march | to border | | `nuif`, `vuf` | bordering | to be a frontier | - |
| `tikan` | upgrade, improvement, attachment, mod, prosthetic | to upgrade, to retrofit, to improve (a tool), to modify | Denotes a machine that improves things, including software patches and cybernetics. | `tik`, `kan` | upgraded | to upgrade, to modify | - |
| `mufif` | mimic, imitator, copier, adopter, memetic recipient | to copy, to imitate, to adopt, to pick up, to emulate | Covers deliberate mimicry as well as things like evolved mimics, unconscious memetic influence, photocopiers, and computer commands. | `muf`, `fif` | mimetic, imitative, derivative, memetic | to copy, to imitate | imitatively |
| `kuvuf` | migrant, immigrant, emigrant, nomad | to migrate to, to relocate to, to immigrate to | Describes someone who is changing the land they call home. | `kuh`, `vuf` | migratory, nomadic, itinerant | to migrate, to relocate, to emigrate, to immigrate, to roam | cross-country |
| `pimum` | nursemaid, mother, wet nurse | to be nursed by, to suckle from | Babies recieve `mumpim` from `pimum`. | `pim`, `mum` | lactating, nursing | to breastfeed, to suckle, to nurse | - |
| `mumpim` | milk, dairy | to be milk for | While the subject in a sentence with this as the verb can be a person, this word carries a connotation of being the physical stuff or the source of that stuff, rather than the person with milk. For that, see `pimum`. | `mum`, `pim` | milky, dairy | to lactate | - |
| `mumuk` | blood, hemolymph, sap, ichor | - | | `mum`, `muk` | bloody, bleeding | to bleed | - |
| `ninuif` | category, ontology, classification, schema, taxonomy, division, order | to categorize, to classify, to distinguish | Where `fif` is about organization (presumably according to natural properties), this is about distinction according to a mental framework. | `niŋ`, `nuif` | categorized, classified, ontological, taxinomical | to categorize, to classify | ontologically, taxinomically |
| `fikuf` | pastel, soft hue | to tint softly, to soften (a color) | | `fik`, `huf` | desaturated, pastel, pale, faded, muted, soft, washed-out | to fade, to blush faintly | softly, mutedly |
| `fikan` | saturated color, highligher | to saturate, to highlight | | `fik`, `kan` | saturated, vivid | to flush | vividly |
| `kimif` | maneuver, course correction | to maneuver, to steer | Notably, the transitive object is the vehicle or thing which is being adjusted, not the target that is being steered towards. | `kim`, `hif` | maneuverable, steerable, corrigible | to maneuver, to steer, to swerve, to veer, to turn | maneuverably, corrigibly |
| `nuifeim` | blur, gradient, transition zone | to blur | | `nuif`, `feim` | blurry, fuzzy, vague | to blur, to blend at the edges | vaguely |
| `kukan` | speedster, accelerator, racer | to accelerate, to speed up, to boost | | `kuh`, `kan` | accelerating, boosted, hurrying, hasty, rushed, quickening | to accelerate, to speed up, to rush | hastily |
| `maukuf` | stuffed animal, pet, fluffy animal, snuggle-buddy | - | Often used by childen and parents to indicate "creatures" that are good for snuggling, whether alive or pretend. | `mauk`, `huf` | fluffy, snuggable, stuffed, fuzzy | to snuggle like a stuffed animal, to snuggle like a pet | - |
| `feimum` | solution, cocktail, brew, potion, infusion, suspension | to dissolve, to stir in, to brew, to mix (a drink) | | `feim`, `mum` | dissolved, soluble, blended | to brew, to mix a drink | - |
| `mukan` | survivor, flourisher, adapter, evolutionary organism | to adapt to, to recover from, to heal from, to grow in the face of, to evolve to handle, to survive | The English word "survivor" carries a connotation that one has faced adversity and held on. This is similar, but with an implication that the adversity led to growth that made the organism (or species) stronger. | `muk`, `kan` | flourishing, recovered, resilient, thriving, robust, tough, resilient, antifragile, evolved | to thrive, to survive, to flourish, to adapt, to recover | thrivingly, evolutionarily |
| `tivuf` | address, lot, grounds, property, homestead, compound, house, premises | to live at (an address), to occupy | Where `tif` refers to a structure and `vuf` refers to a place, `tivuf` is an abstract middle-ground representing a building or set of buildings that are specifically at a place place, a.k.a. address. | `tif`, `vuf` | residential, on-site | to live at an address | - |
| `pifif` | spread, display, arrangement, bouquet, place setting, allotment, dealt hand of cards | to present, to lay out, to deal | The gift of an orderly arrangement: placing things in a deliberate pattern for someone else to take or choose from. Covers a host setting out a meal, a dealer dealing cards, a merchant arranging wares, and a florist’s bouquet. Compare `fif`, which arranges without any recipient in mind. | `pif`, `fif` | presented, arranged, laid out, set out, dealt | to present, to lay out, to deal | - |
| `neimnaun` | happiness, joy, love, tranquility, serenity, ecstasy | to enjoy, to love | | `neim`, `naun` | happy, exuberant, joyous | to be happy, to enjoy oneself | happily |
| `neimuf` | indebtedness, gratitude, appreciation, recognition | to be grateful for (gift/aid), to appreciate, to recognize | Papsa has two words for gratitude. This is the one for the feeling of recieving something unearned, with a special emphasis on noticing the source of a gift. When someone does something kind for you, you're supposed to feel this. Compare `neimfut`, which is about recognizing the blessings of life. | `neim`, `muf` | grateful, appreciative | to be grateful | gratefully, appreciatively |
| `neimfut` | gratitude, appreciation, recognition | to be grateful for (possession/support), to appreciate, to recognize | Papsa has two words for gratitude. This one is for the feeling of being blessed and supported by others and the world more broadly. Gratitude journals are about cultivating this feeling. Compare `neimuf` which is about the specific moment of recieving something, and carries a stronger connotation of being in debt to the giver. | `neim`, `fut` | blessed, grateful, appreciative | to be grateful | gratefully, appreciatively |
| `neimfic` | humor, joke | to laugh at, to find something funny, to be amused by, to enjoy | | `neim`, `fic` | funny, hilarious, amusing | to laugh, to be amused | hilariously, amusingly |
| `neimcun` | sadness, anguish, misery, suffering | to be sad about, to feel bad about | | `neim`, `cun` | sad, miserable, pathetic, depressed | to be sad, to feel bad | sadly, miserably |
| `neimcap` | anger, rage, fury | to be angry about, to rage at, to fight with | | `neim`, `cap` | angry, infuriating | to be angry, to rage | angrily, infuriatingly |
| `neimcin` | fear, horror, dread, worry, terror | to fear, to dread, to worry about | This is a word where the vagueness of `neim` does not map to English very naturally. In most contexts, if it's ambiguous, you should interpret `neimcin` as referring to the one who is afraid (or the experience of fear) rather than externally fearsome things, which in Papsa are more naturally described as directly-bad, rather than implying badness by describing the emotion they invoke. | `neim`, `cin` | afraid, scary, scared, fearful, fearsome, horrified, horrifying, worried, worrying, anxious, terrified, terrifying | to be afraid, to worry | fearfully, fearsomely, horrifyingly, anxiously |
| `neimguk` | pain, injury, anguish | to endure, to suffer from | | `neim`, `guk` | painful, pained | to be in pain, to hurt | painfully |
| `neimcuk` | calm, tranquility, boredom | to sit with, to be bored by | | `neim`, `cuk` | calm, neutral, bored, boring, tranquil | to be calm, to relax | calmly |
| `neimpap` | love | to love | | `neim`, `pap` | lovely, loving | to love | lovingly |
| `neimcapcuk` | hatred | to hate, to despise | | `neimcap`, `cuk` | hated, despicable | to hate | despicably |
| `neimnuŋ` | fascination | to be fascinated by, to be hypnotized by, to get lost in | | `neim`, `nuŋ` | fascinating, fascinated, entranced, hypnotized, oblivious, lost | to be fascinated, to get lost in thought | fascinatingly |
| `neimtif` | visitor, tourist | to visit, to tour | As with `neim`, the noun drifts between the visit as experienced and the one experiencing it. A guest who has come to stay is `nuptifcus`. | `neim`, `tif` | visiting, touring, touristy | to visit, to tour, to sightsee | - |
| `minbik` | hunger | to hunger for | | `min`, `bik` | hungry | to be hungry | hungrily |
| `minbiguk` | starvation, famine, ravenous hunger | to hunger desperately for | A hunger that burns the mind of the one who feels it. | `minbik`, `guk` | starving, famished, ravenous | to starve, to be famished | ravenously |
| `naiskuh` | speed, velocity, pace, rate of travel | to clock, to time, to measure the speed of | Speed as a quantity. To say how fast something goes, `naiskuh` it with a unit: `uaskuh ei sounaum naiskuh li iuman kuh`, "6 Go-Speeds is how fast humans go." | `nais`, `kuh` | clocked, timed, rated | to clock | - |
| `kuzuip` | light speed, full speed, light (as the fastest traveler) | - | The universal speed limit, which ties the units of time to the units of space. | `kuh`, `zuip` | light-speed, full-speed, fastest possible | to go at full speed, to move at the speed of light | at full speed, at light speed |
| `guinaun` | beauty | to attract | | `guih`, `naun` | beautiful, attractive | to be beautiful, to attract | beautifully |
| `guicun` | ugliness | to repel, to repulse, to disgust | | `guih`, `cun` | ugly, gross, repulsive, disgusting | to be ugly, to repel | disgustingly |
| `neimguih` | sight, appearance, look, vision, witness | to see, to watch, to observe, to gaze at, to spot, to notice | Unlike `nuguih`, this focuses mostly on the raw experience of sight, rather than the mental motion of searching. | `neim`, `guih` | visual, visible, watched | to see, to look, to watch | visibly |
| `nuguih` | observer, watcher, watchman, scout, audience, viewer | to look at, to watch, to see, to observe, to gaze at, to search | Unlike `neimguih`, this carries a connotation of active alertness and looking for something. | `nuh`, `guih` | watching, looking, surveilling, attentive, awake, alert, observant | to look, to watch, to observe, to search | - |
| `neimik` | hearing, sound | to hear | If a tree falls in a forest, and nobody is nearby, it makes `hik` but not `neimik`. Unlike `nuhik`, this focuses mostly on the raw experience of sound, rather than the mental motion of listening. | `neim`, `hik` | audible, noisy | to hear | audibly |
| `nuhik` | listener, audience | to listen to | Unlike `neimik`, this carries a connotation of active alertness and paying attention. | `nuh`, `hik` | listening | to listen | - |
| `neimneim` | touching, feeling | to feel, to touch | Mostly used for touching things with skin, but can also be used for felt senses of the body, such as proprioception, stomach aches, stiffness, et cetera. | `neim`, `neim` | tactile, visceral | to feel, to touch | - |
| `nuneim` | - | to read with the hands | Mostly for blind people. Used for Braille, but also can work for a person in a dark room trying to figure out what something is by feeling it. | `nuh`, `neim` | - | to read with the hands | - |
| `neimuh` | scent, smell, odor | to smell | | `neim`, `xuh` | smelly, scented | to smell | - |
| `neimfaip` | taste, sense of taste | to taste | | `neim`, `faip` | gustatory | to taste | - |
| `fifsoun` | timeline, time axis | to arrange chronologically, to position parallel to the zeroth (temporal) axis | | `fif`, `soun` | chronological | - | chronologically |
| `fifsaum` | - | to arrange horizontally, to position parallel to the first axis | There's a connotation of deliberate positioning that is distinct from `taz`. When used to talk about a human lying down, there's an implication that the person is on their side and perhaps holding their head upright. Compare with `fifsat`; contrast with `fifsuk`, `fifsaihat`, and `aihat`. | `fif`, `saum` | horizontal | to lie down | horizontally |
| `fifsuk` | - | to arrange vertically, to position parallel to the second axis | Can be used to talk about a human, tree, or other "standing" thing, but should not be used for the meaning of "stand" that is about existence ("there stood three men..."), since this word strongly implies a deliberately vertical order, rather than an incidental verticality. | `fif`, `suk` | vertical | to stand up | vertically |
| `fifsaihat` | slant, slope | to arrange diagonally, to tilt | Can be used to talk about a human, tree, or similar thing leaning on something, but there's a connotation of deliberate order that implies the angle isn't from a fall. See also: `aihat`. | `fif`, `saihat` | diagonal, leaning, slanted | to tilt, to lean | diagonally |
| `fifsat` | - | to arrange depthwise, to position parallel to the third axis | There's a connotation of deliberate positioning that is distinct from `taz`. When used to talk about a human lying down, there's an implication that the person is on their back (not sitting up or on their side) and perhaps looking towards their toes. Compare with `fifsaum`; contrast with `fifsuk`, `fifsaihat`, and `aihat`. | `fif`, `sat` | deep | to lie down | deeply |
| `hifsoun` | later, futureward | to be later than | Usually just `zeif` or `zouc`. | `hif`, `soun` | later | to go forward in time | later |
| `hifcroun` | earlier, pastward | to be earlier than | Almost always just `zous`. | `hif`, `croun` | earlier | to go back in time | earlier |
| `hifsaum` | right, rightward | to be right of | More commonly `hifpun`. | `hif`, `saum` | right | to go right | rightward |
| `hifcraum` | left, leftward | to be left of | More commonly `hiftaz`. | `hif`, `craum` | left | to go left | leftward |
| `hifsuk` | down, downward | to be below | | `hif`, `suk` | lower | to go down | downward |
| `hifcruk` | up, upward | to be above | More commonly `hifuh`. | `hif`, `cruk` | upper | to go up | upward |
| `hifsat` | forward, front | to be in front of | Also `hiftiŋ`. | `hif`, `sat` | front | to go forward | forward |
| `hifcrat` | back, backward, rear | to be behind | More commonly `hiftuah`. | `hif`, `crat` | rear | to go back | backward |
| `hifpun` | right, rightward | to be right of | | `hif`, `pun` | right | to go right | rightward |
| `hiftaz` | left, leftward | to be left of | | `hif`, `taz` | left | to go left | leftward |
| `hifceim` | down, downward | to be below | Rare. Usually `hifsuk`. | `hif`, `ceim` | lower | to go down | downward |
| `hifuh` | up, upward | to be above | | `hif`, `xuh` | upper | to go up | upward |
| `hiftiŋ` | forward, front, inward | to be in front of, to be further in than | | `hif`, `tiŋ` | front, inner | to go forward, to go in | forward, inward |
| `hiftuah` | back, backward, rear | to be behind | | `hif`, `tuah` | rear | to go back | backward |
| `kimcuk` | sleep, nap, rest | - | Does not demand unconsciousness. Can apply to machines, bacteria, governments, et cetera. | `kim`, `cuk` | sleepy | to sleep, to rest, to be dormant, to be still | sleepily |
| `kimcukcuc` | awakening | - | | `kimcuk`, `juc` | awake | to awake, to be awake, to be active | - |
| `kimneim` | dance | to dance | | `kim`, `neim` | dancing | to dance | - |
| `hikneim` | song, music, audio | to sing, to play, to make sounds for | While not intrinsically about human music, this word captures the act of making noise so that someone else hears it, and an implication that the noise is not just about symbolic words. Crickets chirping, alarms sounding, and poetry all count, though the most central implication is music. | `hik`, `neim` | singing, musical | to sing, to play music | musically |
| `tumpaun` | writer, author | to write | | `tum`, `paun` | authorial | to write | authorially |
| `kuneim` | walker, wanderer, traveler, tourist | to visit, to tour, to walk to | Largely about walking, traveling, and so on for the experience, rather than to pragmatically get somewhere (or for exercise). | `kuh`, `neim` | - | to walk, to stroll, to wander, to travel, to journey, to walkabout | - |
| `bineim` | sense organ, sensor | - | | `bih`, `neim` | sensory | to have the capacity to sense | - |
| `bineimik` | ear | - | | `bineim`, `hik` | aural | to be able to hear, to have ears | - |
| `buptait` | tree, forest, wood | - | | `bup`, `tait` | arboreal | to grow big | - |
| `ficbup` | leaf | - | Implication that the leaves come off the main plant in some sense. Still applies to permanent leaves, but less centrally. | `fic`, `bup` | leafy | to grow leaves, to shed leaves | - |
| `bupfik` | flower | - | | `bup`, `fik` | floral | to bloom, to grow flowers, to sexually mature | - |
| `mainbup` | seed, grain, cereal | to seed | | `main`, `bup` | seeded | to seed, to germinate | - |
| `bupmain` | fruit, nut, plant which is bearing fruit | to bear (as fruit) | Refers to the holder/bearer of seeds, which can either be a full plant or the fruit/nut by itself. | `bup`, `main` | fruity, nutty | to fruit, to bear fruit, to come to fruition | - |
| `ficbupfik` | petal | - | | `ficbup`, `fik` | - | to grow petals | - |
| `ficbupmain` | bud | - | | `ficbup`, `main` | budding | to bud | - |
| `buptaut` | root | to anchor with roots | Most metaphorical uses just use `taut`, unless they're specifically trying to evoke plant roots. | `bup`, `taut` | rooted | to take root | - |
| `bubih` | branch | - | Almost always part of a plant. | `bup`, `bih` | branching | to grow branches, to grow limbs | - |
| `bubihait` | twig, stick | - | | `bubih`, `hait` | twiggy | to grow twigs, to grow fingers | - |
| `bupas` | trunk, stem | - | | `bup`, `has` | - | to grow tall | - |
| `bupasait` | vine | - | | `bupas`, `hait` | - | to grow long | - |
| `bupdup` | bark | - | | `bup`, `dup` | - | to grow armor, to have bark, to have thick skin | - |
| `bupcap` | brambles | - | | `bup`, `cap` | - | to grow brambles | - |
| `buptuah` | thorn, prickle, spine | to prick | | `bup`, `tuah` | thorny, prickly | to grow thorns | - |
| `hasbup` | stick | - | Has the connotation of reaching outward via growth. Vague about what part is grown/stretched. Compare with `bubihait`. | `has`, `bup` | - | - | - |
| `favuf` | soil, dirt, earth | - | | `faf`, `vuf` | soiled, dirty, earthy | - | - |
| `favufsuat` | sand, silt, fine gravel | - | | `favuf`, `suat` | sandy | - | - |
| `fafait` | mote | - | | `faf`, `hait` | - | - | - |
| `fafaitsuat` | dust | - | | `fafait`, `suat` | dusty | - | - |
| `favuftum` | clay | - | | `favuf`, `tum` | - | - | - |
| `favufmum` | mud | - | | `favuf`, `mum` | muddy | - | - |
| `papsoun` | identical twin, clone | to love as yourself | | `pap`, `soun` | - | to love another as you love yourself | - |
| `papsaum` | parent, child, sibling, brother, sister, mother, father, kid, nuclear family member | to love as a brother, to love as a sister, to love as a parent, to love as a child | | `pap`, `saum` | - | to love as a brother, to love as a sister, to love as a parent, to love as a child | - |
| `papsuk` | grandparent, grandchild, grandkid, half-sibling, niece, nephew, nibling, uncle, aunt | to love as family | | `pap`, `suk` | - | to love as family | - |
| `papsat` | cousin | to love as a cousin | | `pap`, `sat` | - | to love as a cousin | - |
| `papsuat` | family, clan | to love as family | | `pap`, `suat` | clannish | to love as family | - |
| `pabih` | sibling, brother, sister | to love as a brother, to love as a sister, to love as a sibling | Used metaphorically more often than `papsaum`. | `pap`, `bih` | brotherly, sisterly, fraternal, sororal | to love as a brother, to love as a sister, to love as a sibling | - |
| `papdup` | parent, guardian | to love as a parent | Also applies to step-parents, foster parents, et cetera. | `pap`, `dup` | parental | to love as a parent | - |
| `papmain` | child, kid | to love as a child, to care for (a child) | The verb form also applies to teacher/student relationships and even peer relations between kids. Does *not* (usually) apply to grown people who are descended or were raised by someone. More affectionate than `mainup`. | `pap`, `main` | childish | to care for a child, to love a child | - |
| `mainup` | child, kid, boy, girl, baby, toddler | - | Generic way to refer to a young person. Doesn't have as much loving connotation as `papmain`. | `main`, `nup` | childish | to be childish, to be innocent, to play, to be young in nature | childishly |
| `mainupmain` | baby, infant | - | | `mainup`, `main` | infantile | to be a baby | - |
| `bikmum` | seafood, fish, shellfish | - | Technically also includes freshwater fare and seaweed. | `bik`, `mum` | - | to eat seafood | - |
| `bikbup` | produce, vegetable, fruit | - | | `bik`, `bup` | - | to eat produce | - |
| `bikmauk` | meat, flesh | - | | `bik`, `mauk` | carnivorous, meaty | to eat meat | - |
| `vinjuc` | there, then, that | to leave, to exit, to depart from | | `vin`, `juc` | distant, far, away | to depart, to leave, to walk away | distantly |
| `bibih` | subdivision, finger, toe | to subdivide | | `bih`, `bih` | subdivided | to subdivide, to sub-specialize | subdividedly |
| `faftaz` | equality, identity | to equal, to balance, to be the same as | | `faf`, `taz` | equal, balanced, identical, matched | to be equal, to balance | equally, identically |
| `kufut` | rider, passenger | to be carried to, to ride to | | `kuh`, `fut` | riding | to ride, to go by vehicle | - |
| `hasait` | thread, string, strand, filament, fiber, yarn, twine, cord, hair | to string, to thread, to spin, to draw out thin, to stretch into a strand | Where `has` leans toward sturdy lines like sticks, rods, and tubes, `hasait` is the thin, flexible kind: the sort of line that can be wound, knotted, or woven. Follows the same diminutive pattern as `bupasait` (vine) and `bubihait` (twig). Hair, silk, sinew, and optical fiber all count, whatever they're made of. A natural base for further compounds, such as rope, cable, or fabric. | `has`, `hait` | threadlike, stringy, fibrous, wispy, fine, thin | to spin | finely, thinly |
| `hasaidut` | wire, metal thread, metal filament, metal string | to wire, to bind with wire, to string with wire, to draw metal into wire | Metal drawn into a `hasait`, so it keeps the flexibility of thread and doesn't take on the hardness `dut` usually implies. Says nothing about electricity: fencing wire, jewelry wire, and piano strings are all `hasaidut`. An electrical cable would more naturally be a compound built on `gaug`. The internal "td" collapses to "d," following the right-side preference. | `hasait`, `dut` | wiry, wired, wire-bound | to wire | - |
| `hasaitfic` | fiber, fibril, staple, strand, loose thread | to fray, to unravel, to shred into strands, to tease apart | The pieces a `hasait` is made of, or comes apart into. Thread is spun from `hasaitfic`, and a frayed rope end is a tangle of it. Covers cotton and wool fibers, the grain of wood, muscle fibers, and the strands of a torn leaf. Where `hasait` is a strand considered as a whole line, `hasaitfic` is a strand considered as a component of something larger. Dietary fiber is most naturally `bik ei hasaitfic`. | `hasait`, `fic` | fibrous, frayed, shredded, fibrillar | to fray, to unravel | - |
| `tikmuk` | biomachine, biological mechanism, cellular machinery | - | The machinery of life. The compound deliberately sets aside the "artificial" connotation of `tik`; Papsa draws no fundamental line between machines that are made and machines that are grown, and this word only says whose machine it is. A `tikmuk` need not be alive itself, which is why the most literal phrase is `tik ui muk`, while `tik ei muk` instead describes a machine that is itself alive. Body parts considered as parts are more naturally `bih`; `tikmuk` emphasizes the mechanistic aspect. | `tik`, `muk` | biomechanical | to function mechanically within an organism | biomechanically |
| `tikmukait` | molecular machine, ribosome, enzyme, motor protein, ATP synthase, flagellar motor | to catalyze, to assemble molecules for, to process at the molecular scale | The smallest `tikmuk` -- individual molecules or complexes that do mechanical or chemical work. | `tikmuk`, `hait` | biochemical, molecular, nanoscale | to catalyze | - |
| `ficait` | atom, element, molecule, particle | to disintegrate | A generic way to talk about particles. | `fic`, `hait` | molecular, atomic | to disintegrate | - |
| `guixarc` | flash, flare, glint, burst of light, camera flash | - | A light that shines and is gone. Implies a single brief event; a light that keeps flashing is `guimaz`. The flash of lightning is a `guixarc`, though the lightning itself is `gaug`. | `guih`, `xarc` | flashing, momentary, glinting | to flash, to flare, to glint, to flicker once | momentarily, in a flash |
| `guimaz` | blinker, strobe, beacon, lighthouse, signal lamp, turn signal | to signal with light | A light with the character of a cycle, the mirror image of `mazguih`, which is a cycle with the character of light. The noun is the thing that blinks rather than the blinking itself, in keeping with the "ui" vowel of `guih`. Covers regular rhythms (a turn signal, a pulsar) and irregular ones (a failing bulb), as long as the light keeps returning. | `guih`, `maz` | blinking, strobing, pulsing, intermittent | to blink, to strobe, to pulse, to flash on and off | intermittently, rhythmically |
| `cinarc` | blink, momentary blackout, lapse, flicker, gap | - | A fleeting concealment: darkness that comes and immediately goes. Most often used for blinking the eyes, described from the inside as a moment of darkness rather than as the motion of a body part. Also covers a flicker in a signal, a brief power outage, or a gap in attention. The inverse of `guixarc`: a flash is a brief light, a blink is a brief dark. | `cin`, `xarc` | lapsing, flickering, intermittent | to blink, to black out momentarily, to flicker out, to lapse briefly | in a blink, instantly |
| `bikuh` | breath, inhaled air, respiration | to breathe in, to inhale | Literally "to consume air." Not tied to lungs: any organism taking in gas qualifies, from a fish drawing oxygen through its gills to a plant absorbing carbon dioxide. Fire is often said to `bikuh`, extending the sense of `bik` as fuel. Centrally about inhalation; breathing out is more naturally a compound built on `pif`. Compare `xukim` (moving air) and `neimuh` (smelling, which a breath often carries). | `bik`, `xuh` | breathable, respiratory, breathing | to breathe, to respire, to draw breath | - |
| `naunais` | preference, favorite | to be preferred by, to be the preference of | `naun` on its own has a connotation of an objective sort of good. Compounding with `nais` says that the good is in a mind's judgment. | `naun`, `nais` | preferred, favorite | to be preferred, to be a favorite | preferably |
| `zouctait` | - | to be bigger than, to outsize | | `zouc`, `tait` | bigger, larger | to be bigger | - |
| `zoujuctait` | - | to be smaller than | While a compound of `zous` and `tait` would be in some sense more natural, we use the double negative (not greater in size) to keep the words from sounding too similar. | `zouc`, `juc`, `tait` | smaller, lesser | to be smaller | - |
| `paunup` | name | to name, to be the name of | | `paun`, `nup` | named, nominal | to be a name, to be a namer | by name |
| `mazguimain` | dawn, morning, daybreak, sunrise | to dawn on | | `mazguih`, `main` | early, matutinal | to dawn | at dawn |
| `zusmuk` | age, lifetime | to be the age of (a living thing) | | `zus`, `muk` | aged | to be an age | - |
| `suatsuat` | myriad, countless multitude | - | Finite, unlike `suaf`: countable in principle, but not in practice. | `suat`, `suat` | countless, innumerable, myriad | to outnumber counting, to be countless | innumerably |
| `sisuin` | - | to be able to list, to be able to count | | `sih`, `suin` | countable, listable, enumerable | to be able to list, to be able to count | countably |
| `haitsuaf` | infinitesimal | to shrink without end | | `hait`, `suaf` | infinitesimal | to shrink without end | infinitesimally |
| `fafjuc` | inequality | to be unequal to, to differ in value from | | `faf`, `juc` | unequal | to be unequal | unequally |
| `zousaum` | immediate predecessor | to immediately precede, to be one less than | | `zous`, `saum` | immediately preceding | to come immediately before | - |
| `zoucsaum` | successor | to immediately follow, to be one more than | | `zouc`, `saum` | immediately following | to come immediately after | - |
| `zousis` | - | to be less than | | `zous`, `sis` | lesser | to be less | - |
| `zousfaf` | - | to be less than or equal to, to be at most | | `zous`, `faf` | - | to be at most | - |
| `zoucfaf` | - | to be greater than or equal to, to be at least | | `zouc`, `faf` | - | to be at least | - |
| `tikpaun` | variable, assumption, pointer, placeholder | to substitute for, to fill in for, to be plugged in for | | `tik`, `paun` | variable, assumed | to be a substitute, to fill in | - |
| `mamtik` | algorithm | to compute | | `mam`, `tik` | algorithmic | - | algorithmically |
| `mamzuip` | function | to be a function of | In most mathematical contexts, plain `mam` is used. | `mam`, `zuip` | functional | to be a function | functionally |
| `mamas` | arrow, morphism | to map to | An arrow in category theory. | `mam`, `has` | - | to map | - |
| `vufih` | outside, outdoors, open air | to be the outside of | | `vuf`, `xih` | outdoor, outside, external | to be outside | outdoors, outside |
| `vinzus` | now, the present, present moment | to be the present moment of | Unlike `vin`, which can mean here or now, `vinzus` is only about time. | `vin`, `zus` | current, present, contemporary | to be the present | now, currently, presently |
| `vufpip` | market, marketplace, bazaar, exchange | to be the market of | | `vuf`, `pip` | commercial, mercantile | to be a market | - |
| `vufpipzeif` | prediction market, betting market | to be the prediction market of | | `vufpip`, `zeif` | - | to be a prediction market | - |
| `vufbik` | farm, field, farmland, cropland | to be the farmland of | | `vuf`, `bik` | agricultural, arable | to be farmland | - |
| `vufbikmauk` | pasture, meadow, grazing land | to be the pasture of | | `vufbik`, `mauk` | pastoral | to be pasture | - |
| `vufbikvuf` | countryside, the country, rural land | - | | `vufbik`, `vuf` | rural, rustic | - | - |
| `vuftif` | village, town, settlement, hamlet | to be the village of | | `vuf`, `tif` | settled, small-town | to be a village | - |
| `vuftiftait` | city, metropolis | to be the city of | | `vuftif`, `tait` | urban, civic, metropolitan | to be a city | - |
| `nupvuf` | neighbor, local | to live near, to neighbor | | `nup`, `vuf` | neighborly, local | to be local, to live nearby | - |
| `nuptif` | resident, inhabitant, occupant, roommate, housemate | to inhabit, to reside in, to occupy, to live in | | `nup`, `tif` | resident, residential | to dwell, to reside | - |
| `nuptifcus` | guest, visitor, lodger | to stay as a guest at, to lodge with | A stranger who has come to stay. Compare `neimtif`, the one passing through to see a place. | `nuptif`, `cus` | visiting | to lodge, to stay as a guest | - |
| `tifait` | hut, shack, cabin, shed | - | | `tif`, `hait` | - | - | - |
| `taudut` | wall | to wall off, to wall in | | `taut`, `dut` | walled | to wall off | - |
| `dutaut` | fence, barrier, railing | to fence in, to enclose | | `dut`, `taut` | fenced | to fence in | - |
| `taztaut` | floor, flooring | to floor | | `taz`, `taut` | - | to floor | - |
| `tautuf` | bed, mattress, cushion | to be the bed of | | `taut`, `huf` | - | to be a bed | - |
| `tazdup` | lid, cover, cap | to cover, to cap, to put a lid on | | `taz`, `dup` | covered, capped | to cover, to cap | - |
| `futbup` | basket, hamper | - | | `fut`, `bup` | - | - | - |
| `fiftaut` | frame, framework, scaffold, chassis | to frame, to brace | | `fif`, `taut` | structural, framed | to frame, to brace | - |
| `mutaut` | skeleton | to hold up (a body) | | `mut`, `taut` | skeletal | to hold up a body | - |
| `vufmaz` | hill, slope, mound, rise | - | | `vuf`, `maz` | hilly | to slope | - |
| `vufbupmuk` | jungle, rainforest | - | | `vuf`, `bup`, `muk` | - | - | - |
| `vufbupnaun` | park, garden, green | - | | `vuf`, `bup`, `naun` | - | - | - |
| `mumaz` | wave, swell, surge | - | | `mum`, `maz` | wavy, undulating | to undulate, to surge | - |
| `mumaztait` | tide | - | | `mumaz`, `tait` | tidal | - | - |
| `dutait` | boulder, crag | - | | `dut`, `tait` | - | - | - |
| `dutficait` | pebble, gravel, grit | - | | `dut`, `fic`, `hait` | pebbly, gravelly | - | - |
| `gukait` | spark, ember, flicker | - | A tiny fire. Compare `gaug`, the electric spark. | `guk`, `hait` | sparking | to spark, to flicker | - |
| `hasmain` | ray, beam | to radiate, to beam | | `has`, `main` | radiant | to radiate, to beam | - |
| `bazas` | knot, tangle | to knot, to tie, to tangle | | `baz`, `has` | knotted, tangled | to knot, to tangle | - |
| `minmauk` | prey, quarry, game | to hunt, to stalk, to prey on | Hunting for prey, where `min` is any seeking. | `min`, `mauk` | predatory | to hunt, to stalk | - |
| `hikdut` | echo, reverb, reverberation | - | Sound reflected off hard surfaces, where `hik` is any sound. | `hik`, `dut` | echoing, reverberant | to echo, to reverberate | - |
| `hikait` | whisper, murmur | to whisper | | `hik`, `hait` | whispered, hushed | to whisper, to murmur | - |
| `hikcuc` | silence, quiet, hush | to hush, to silence | | `hik`, `juc` | silent, quiet, hushed | to fall silent, to hush | silently, quietly |
| `mukcuc` | decay, rot, decomposition | - | | `muk`, `juc` | decaying, rotten, decomposing | to decay, to rot, to decompose | - |
| `gukmuk` | strength, vigor, vitality | to strengthen, to invigorate | | `guk`, `muk` | strong, vigorous, hale | to invigorate | strongly, vigorously |
| `cukmuk` | weakness, frailty, feebleness | to weaken, to sap | | `cuk`, `muk` | weak, frail, feeble | to sap | weakly, feebly |
| `kukuh` | runner, sprinter | to run to, to dash to | | `kuh`, `kuh` | running, fleet | to run, to dash, to sprint | - |
| `mumbik` | drink, beverage | to quench the thirst of | The one drinking is the object: `mum mumbik fila` (the cat drinks water). | `mum`, `bik` | thirst-quenching | to quench thirst | - |
| `muksuk` | sex (biological) | to express the (biological) sex of | | `muk`, `suk` | sexed, sex-linked | to express a sex | - |
| `muksukpif` | male | - | | `muksuk`, `pif` | male | to be male (as reflected by a trait) | - |
| `muksukfun` | female | - | | `muksuk`, `fun` | female | to be female (as reflected by a trait) | - |
| `nupmuksukpif` | masculinity | - | | `nup`, `muksukpif` | masculine, manly | to be masculine | - |
| `nupmuksukfun` | femininity | - | | `nup`, `muksukfun` | feminine, womanly | to be feminine | - |
| `funzusmuksukfun` | menarche, first period | to be the menarche of | | `fun`, `zus`, `muksukfun` | - | to be a menarche | - |
| `papfun` | infatuation, crush | to be infatuated with, to have a crush on | | `pap`, `fun` | infatuated, smitten | to be infatuated, to have a crush | - |
| `papfuncin` | affair, adultery, infidelity, secret romance | to have an affair with | | `papfun`, `cin` | adulterous, unfaithful | to have an affair | - |
| `zusnunuh` | school, class, lesson, lecture | - | School as an event. `vufnunuh` is the school as a place, and `tifnunuh` is its building. | `zus`, `nuŋ`, `nuh` | scholastic | to be in session | - |
| `vufnunuh` | school, academy, campus | - | | `vuf`, `nuŋ`, `nuh` | academic | - | - |
| `tifnunuh` | schoolhouse, school building | - | | `tif`, `nuŋ`, `nuh` | - | - | - |
| `ninais` | mathematics, math | to calculate | | `niŋ`, `nais` | mathematical | to calculate, to do mathematics | mathematically |
| `sibaz` | category (mathematics) | - | | `sip`, `baz` | categorical | - | - |
| `nupapsnuh` | detective, investigator, inspector | to investigate, to detect | | `nup`, `paps`, `nuh` | investigative | to investigate, to detect | - |
| `mufcin` | trickster, con artist, swindler | to trick, to con, to swindle | | `muf`, `cin` | tricky, deceptive | to trick, to con, to swindle | deceptively |
| `kimtum` | work, labor, job, task | to work on | | `kim`, `tum` | working, laborious | to work, to labor | - |
| `kudvin` | puller | to pull, to draw, to drag | | `kud`, `vin` | pulling | to pull, to draw, to drag | - |
| `tikdup` | clothing, clothes, garment, outfit | to clothe, to dress | | `tik`, `dup` | clothed, dressed | to dress | - |
| `tikdupdut` | armor | to armor | | `tikdup`, `dut` | armored | to armor | - |
| `tiknup` | doll, puppet, action figure | to play with (a doll), to pose | | `tik`, `nup` | - | to play with dolls | - |
| `duptik` | shield | to shield | | `dup`, `tik` | shielding | to shield | - |
| `neimam` | adjustment, acclimation, habituation, change of heart | to get used to, to adjust to, to acclimate to, to grow accustomed to, to come around to, to warm to, to be moved by | Eyes adjusting to darkness, cold water that stops feeling cold, warming to a song you once disliked, or coming to feel that something that was once strange is now normal. | `neim`, `mam` | accustomed, acclimated, adjusted, habituated, moved, touched | to adjust, to acclimate | - |
| `higuk` | din, racket, blare, screech, cacophony, noise pollution | to deafen, to grate on, to assault with noise | A sound that "burns", the way `neimguk` is a feeling that burns. The burn is in the hearing, not necessarily the volume: fingernails on a chalkboard are `higuk` at any loudness. | `hik`, `guk` | loud, deafening, shrill, piercing, grating, harsh, ear-splitting | to blare, to screech, to clang, to roar | deafeningly, shrilly |
| `higukpun` | scream, shriek, howl, wail, roar, bellow, war cry, outcry | to scream, to shriek, to howl | A `higuk` that expresses something. Because the base is the sound, the noun is the cry itself, and words are optional: a scream of terror, a baby's wail, a wolf's howl and a crowd's roar all count. Compare `puniguk`, where the speaker and their message come first. | `higuk`, `pun` | screaming, shrieking, howling | to scream, to shriek, to howl, to cry out | - |
| `punik` | voice, vocalist, announcer, narrator, reader-aloud | to say aloud, to voice, to utter, to pronounce, to vocalize, to read aloud | Where `pun` covers expression through any channel (writing, gesture, art), `punik` is expression through sound. Words aren't required, so a whistled signal or a meaningful grunt counts. Where `hikneim` is sound made to be heard, `punik` is sound used to get something across. | `pun`, `hik` | vocal, spoken, oral, verbal | to speak aloud, to vocalize | aloud, out loud, orally, vocally |
| `puniguk` | shouter, yeller, loudmouth, crier, barker, herald | to shout, to yell, to holler, to call out, to bellow | Speech turned up until it burns. It usually carries a message, as when shouting directions across a field, but the intensity implies anger or urgency. A shouting match is `puniguk` on both sides. Compare `higukpun`, where the noise comes first and words are optional. | `punik`, `guk` | loud, shouting, strident, vociferous, heated | to shout, to yell, to raise one's voice | loudly, vociferously, at the top of one's lungs |
| `sipfic` | subset, portion, share, selection, contingent, faction | to take some of, to select from, to sample, to split off a share of | Part of a collection: more than one but less than the whole group, with no claim about how large a part. | `sip`, `fic` | some, several, partial, certain, select, a few | to split off, to sample from a group | partly, partially, in part, sometimes |
| `zeivin` | the imminent, what's coming, near future, the impending | to see coming, to expect any moment | A fate that has nearly arrived. Like `zeif`, it carries confidence that the thing really is coming; it just puts it close. As with `vin`, it can stretch to space, for a destination coming into view. | `zeif`, `vin` | imminent, impending, upcoming, forthcoming, near, close at hand | to be imminent, to loom, to draw near, to be about to happen | soon, shortly, momentarily, any moment now |
| `kumaz` | roller, wheel, rolling stone, tumbleweed | to roll to, to roll toward | Travel by turning over and over, like a ball or a wheel. To roll something else, make it the object of a causal verb like `kud`. | `kuh`, `maz` | rolling, tumbling, wheeled, wheeling | to roll, to tumble, to trundle, to wheel | end over end, head over heels |
| `zeific` | odds, probability, likelihood, chance, likely outcome, best guess | to expect, to bet on, to deem likely, to be the odds of, to be the probability of | An outcome foreseen, but without the certainty of `zeif`. When used in many contexts, there's an implication of relative likelihood compared to other outcomes (though it might simply be the plurality rather than >50% probability). In more precise contexts, the subject can be the numeric odds or probability of the outcome. See "Credence, Speculation, and Theory" in the Philosophy appendix for more. | `zeif`, `fic` | probable, likely, plausible, expected | to be likely, to be probable, to seem likely | probably, likely, presumably |
| `punuh` | we, us | to discuss, to talk about | Explicitly inclusive of both the speaker and the listener. When used as a verb it implies the subject is multiple people talking amongst themselves. | `pun`, `nuh` | - | to discuss, to talk | - |
| `nupun` | artist, creative, poet, performer, auteur | to express oneself through, to make art in | A person who lives by expressing themselves. Usually implies profession/vocation, but can apply to very passionate amateurs. `pun` is more general -- anyone expressing anything. Compare `tumpaun`. | `nup`, `pun` | artistic, creative, expressive | to make art, to create, to be an artist | artistically, creatively |
| `minif` | missing way, the way back | to look for the way to, to seek a route to | A dog that has wandered off is `minif`, while its owner has `cif` it. Extends readily to being lost in life or in conversation. | `min`, `hif` | lost, disoriented, astray, adrift, bewildered, stray | to be lost, to lose one's way, to wander, to stray | aimlessly, astray, blindly |
| `zouscun` | - | to be too early for, to come too quickly | Can have sexual connotations. | `zous`, `cun` | premature | to be unfortunately early | prematurely |
| `zousnuif` | eldest, oldest, firstborn, doyen, senior member, earliest example, dawn of time | to be the oldest of, to predate all of | | `zous`, `nuif` | oldest, eldest, earliest, first | to be the oldest | first, earliest |
| `tifarc` | tent, camp, campsite, shelter, lean-to, bivouac, encampment | to camp at, to pitch camp at, to bivouac in | A dwelling meant to come down again. A tent, a lean-to, a snow cave, a refugee camp, or a festival's field of yurts all count. What makes it `tifarc` is that it's temporary, not what it's made of. | `tif`, `xarc` | makeshift, temporary, portable, pop-up | to camp, to pitch camp, to rough it | - |
| `nuptifarc` | camper, backpacker, bivouacker, nomad | to camp at, to camp in | Someone living for now in a dwelling they will take down again. Compare `nuptifcus`, who stays in someone else's home, and `kuvuf`, who is changing homes for good. | `nuptif`, `xarc` | camping, encamped | to camp, to go camping | - |
| `mazcuk` | winter, cold season, off-season, lull, low ebb, dormant period | to be the wintertime routine of | The cold, slow stretch of a cycle -- usually winter, but can be used more generally to any periodic dormancy. | `maz`, `cuk` | wintry, hibernal, dormant, fallow | to be a wintery thing | - |
| `mazguk` | summer, hot season | to be the summertime routine of | The hot, painful stretch of a cycle -- usually summer, but can theoretically be used in other contexts. | `maz`, `guk` | summery | to be a summertime thing | - |
| `mazuh` | spring, rising season | to be the springtime routine of | The rising, awakening part of a cycle -- usually spring, but can theoretically be used in other contexts when activity is growing. | `maz`, `xuh` | vernal | to be a springtime thing | - |
| `mazarc` | fall, autumn, vanishing season | to be the autumn routine of | The fading, disappearing part of a cycle -- almost always autumn or the opposite of some other kind of `mazuh`. | `maz`, `xarc` | autumnal | to be an autumn thing | - |
| `zusik` | concert, recital, performance | to be the concert of | Music as an event, the way `zusnunuh` is school as an event. Technically this doesn't have to be music, but in practice it's implied. | `zus`, `hik` | - | to be a concert | - |
| `kumum` | swimmer, sailor, mariner, seaman, passenger, boat, ship, raft | to sail to, to swim to, to row to, to float to | Travel by water, by whatever means. When used to describe swimming, this implies travel, not just moving around in the water. (See `maukmum`.) The subject is assumed to be an agent, so when it's used as a noun it rarely means "boat" as a passive object, but can mean "boat" in the same way English anthropomorphizes boats in sentences like "the boat sailed across the lake." To talk about ships/boats/rafts as objects, use `tikumum`. | `kuh`, `mum` | seafaring, swimming, nautical | to sail, to travel on water, to travel in water, to swim somewhere | - |
| `tikuh` | vehicle, craft, conveyance, transport | to drive, to ride, to be carried in | See also: `kufut`. | `tik`, `kuh` | vehicular | to drive, to ride | - |
| `tikumaz` | car, cart, wagon, bus, truck, bicycle, wheeled vehicle | to drive, to ride, to be carried in | | `tikuh`, `maz` | wheeled | to drive, to cycle | - |
| `tikumum` | boat, ship, raft, ferry, submarine | to sail, to be ferried in | See also: `kumum` and `maukmum`. | `tikuh`, `mum` | boatlike, nautical | to sail | - |
| `tikuxuh` | aircraft, airplane, airship, helicopter, spacecraft | to fly in | See also: `kuxuh` and `maukuh`. | `tikuh`, `xuh` | aeronautical | to fly in a vehicle | - |
| `mumkuzeif` | downstream, river mouth, downriver | to flow toward | | `mumkuh`, `zeif` | downstream, downriver | to flow downstream | - |
| `nupzuip` | everyone, everybody, all people | - | | `nup`, `zuip` | universal, common to everyone | - | - |
| `kumain` | - | to set out for, to start toward | | `kuh`, `main` | departing, outbound | to set out, to depart, to embark | - |
| `pifcin` | trick, prank, illusion, stage magic, sleight of hand | to play the trick of, to perform the illusion of | A useful noun to contrast with the trickster of `mufcin`. When used as a transitive verb, the object is the trick, rather than the one who is tricked. Carries a slightly more playful/harmless tone than `mufcin`. | `pif`, `cin` | tricky, mischievous, illusory, magical | to play tricks, to clown around, to do stage magic | mischievously |
| `nupzous` | elder, old person, senior | to be older than | | `nup`, `zous` | old, elderly, aged | to grow old, to age | - |
| `houtik` | hat, lid, covering, cap, crown, top | to cover | | `hout`, `tik` | - | to be a hat | - |
| `houtikmaz` | circlet, headband, crown, tiara, wreath | - | | `houtik`, `maz` | - | - | - |
| `zusguidut` | solstice | - | | `zus`, `guih`, `dut` | solstitial | - | - |
| `guktait` | bonfire, blaze, conflagration | to engulf in flames | | `guk`, `tait` | blazing | to blaze, to rage | - |
TODO: Body Parts, Buildings, Towns, and Cities
for (const [wrd,row] of compoundMeanings["Word"]) addCoreWord(wrd, row, 'compoundMeanings');
// Link each compound and named dictionary entry with the words it comes from, both ways, so a lookup can
// show either. A source can also be a number word, which has no dictionary entry, so what comes from each
// word is kept here, under the base letters of the word it reads as.
const derivedFrom = { compounds: {}, names: {} };
const linkSources = (entry, field, sources, kind, where) => {
entry[field] = sources;
for (const s of sources) {
const src = makeToken(s);
if (src.probablyForeign) { tableErrors.push({ where, reason: `${entry.word} comes from ${s}, which isn't a word` }); continue; }
const list = (derivedFrom[kind][baseLetters(src.word)] ??= []);
if (!list.includes(entry.word)) list.push(entry.word);
}
};
for (const [wrd,row] of compoundMeanings["Word"]) {
const entry = coreWords[baseLetters(wrd)];
if (entry.word === wrd) linkSources(entry, 'roots', row.code['Roots'], 'compounds', 'compoundMeanings'); // else a collision, already reported
}
for (const [name,row] of papsaNames["Papsa Name"]) {
const entry = names[baseLetters(name)];
if (entry.word === name) linkSources(entry, 'etymology', row.code['Etymology'], 'names', 'papsaNames');
}
for (const ws of Object.values(derivedFrom).flatMap(Object.values)) ws.sort();
Papsa was developed by Max Harms, who also blogs about Utopia at utopiandreams.substack.com. To suggest improvements to the language or have your own Papsa resource linked here, contact Max by any of the methods listed on his personal website. You can also submit pull requests on GitHub.
Everything on this website, including the font for rendering Papsa text, is released into the public domain and is free for anyone to use.