jev-facade #
Façade (2005) with its text parser replaced by Jev, a decision model that returns a typed choice with a probability instead of text.
Façade never tried to understand sentences. It sorts whatever you type into one of about 35 social moves (agree, criticize, flirt, take a side, bring up the wedding photo...) and the whole drama runs on those. The sorting was done by a few thousand hand-written pattern rules, and it is the part of the game that aged worst: famously, it cannot tell what you mean most of the time.
This project swaps only that step. Your line goes to Jev, Jev picks the move, and the game's own reaction logic, story beats and drama manager carry on exactly as they were written, now reacting to what you actually said.

Quick start #
You need three things, all free to get:
| Façade 1.1 for Windows | Free from its authors: playablstudios.com/facade. Install it to the default folder. |
| Python 3.9+ | python.org/downloads. Tick "Add python.exe to PATH" in the installer. Nothing else to install: the sidecar is standard library only. |
| An OpenRouter API key | Make one at openrouter.ai/keys and add a little credit at openrouter.ai/credits. Each line you type costs a fraction of a cent. |
Then:
- Get this repo with
git clone. - Double-click
install.bat. It asks for administrator rights (Façade lives inProgram Files) and patches the game. Every file it replaces is kept beside it as<name>.orig. - Double-click
play.bat. The first time, it creates a.envfile and opens it: paste your key afterOPENROUTER_API_KEY=, save, and runplay.batagain.
From then on play.bat is all you run. It starts the sidecar, opens the
monitor in your browser and launches Façade. Leave its window open while you
play, and type to Trip and Grace as usual.
To put the original game back: install.bat --uninstall. If Façade is not in
C:\Program Files (x86)\Facade, give both scripts the folder
(install.bat "D:\Games\Facade") or set a FACADE_HOME environment variable.
How it fits together #
Façade (bundled Java 1.4) --plain HTTP, loopback--> sidecar (Python) --HTTPS--> OpenRouter / Jev
The sidecar exists because the Java runtime the game ships with is too old to
make a modern TLS connection, and because it keeps your API key out of the game
folder. If the sidecar is not running, or Jev cannot be reached, the patched
game falls back to its original parser line by line, so it is always playable.
The sidecar always listens on 127.0.0.1:8765; that address is compiled into
the game patch, so it is not a setting.
This repository contains no game files. You need your own copy of Façade,
which its authors, Michael Mateas and Andrew Stern, released as freeware. This
project is not affiliated with them or with TypeSafe. It was written against
Façade 1.1 (the 2006 Windows installer: util/j2re1.4.2_06, loose class files
in util/classes) and is Windows only, like the game.
The pieces play.bat and install.bat wrap, if you would rather run them
yourself:
python -m sidecar.repl # type lines, see the moves; no game needed
python -m sidecar.server # just the sidecar; monitor at http://127.0.0.1:8765/
python tools/install_patch.py --status # no flag installs, --uninstall removes; needs an admin terminal
The monitor #
Every line the game sends shows up live: what you typed, the moves sent back (orange outline means said to Grace, blue to Trip), the round-trip time, and an amber "deflect" when Jev did not find a move. Click a line for Jev's top options on every question, what Trip and Grace had just said, which situation the game reported, and the exact integers handed to the game. The box at the top sends a line through the same path without the game.
How a line becomes moves #
The game's internal name for a move is a discourse act: five integers, an act
type, who it was said to, and up to three parameters. sidecar/da_schema.py is
that vocabulary as data, with a description of each option written for Jev.
One request asks Jev several questions about the same state. The state is your
line (with trip/grace capitalised, because lowercase "trip" reads as a
noun), a sentence describing the scene, what Trip and Grace just said, and what
the game says is happening (its active parser contexts, translated to plain
English by sidecar/contexts.py). The questions:
act: which move, orotheraddressee: Grace, Trip or bothreference: which topic or object the line brings up, ornone- one question per parameter family: emotion, question word, trait or advice, and who/relation/whom for the big accusations
The reference is its own question because it co-occurs with any act: "I love your couch" is praise and a reference to the couch.
A choice is accepted on the probability of the winner, not on Jev's confidence
score. Confidence measures how peaked the distribution is, so two near-synonyms
splitting the vote (0.51 oppose, 0.33 pacify) look like low confidence when
they are really two good readings. The sidecar takes the winner, and deflects
only when other wins or the winner is a weak plurality.
Output follows the game's own rules, read from its rule files: a reference is
never addressed to anyone and its question word defaults to why; the big
accusation is (relation, who, whom); and a SystemDoesntUnderstand act rides
along with every line, as it does in the original, so a beat with no reaction
to the real act deflects instead of going silent.
What the patch changes in the game #
The game starts its Java side as a separate java.exe on a bundled Java 1.4.2,
with its classes as loose files, so single classes can be swapped. The patch
replaces three and adds three (tools/patch_manifest.py is the full list):
TemplateCompiler.Preprocessorturned a typed line into word facts for the rule engine. The replacement asks the sidecar instead and asserts the resulting(DA type char p1 p2 p3)facts itself; the game's own listener turns those into the objects its story logic reads. The original class is kept, renamed inside its bytecode toPreprocessorRules, and handles any line the sidecar cannot answer. That renamed class is the game's own code, so it is not in this repo: the installer makes it from your copy.facade.primact.PushTripDialogAnimationandPushGraceDialogAnimationare the acts every spoken line goes through. The replacements do what the originals did and also note the line, so Jev can be told what you are replying to. The game has no subtitle text, but it names every recorded line after its words (..._FASKDRINKT1NTPA_how1_does_a_martini_sound), andsidecar/dialogue.pyturns that intoTRIP: "how does a martini sound", keeping stage directions such asFURIOUSas the tone.
The five classes of ours ship compiled in patch/prebuilt/, which is why
installing needs no JDK. The sources are in patch/src/.
Development #
python -m unittest discover -s tests # no key needed: runs against a fake OpenRouter
python -m sidecar.evaluate --misses # live: scores Jev on eval/cases.tsv, a request per line
python tools/extract_rules.py # decompress the game's rule files into vendor/ (gitignored)
python tools/show_rules.py MapToDA # read them
Changing patch/src is the one thing that needs a JDK (9 to 21, for example
Adoptium) on PATH:
python tools/build_patch.py # compile, retarget to Java 1.4, refresh patch/prebuilt/
python tools/test_patch.py # run the result on the game's own JVM (start the sidecar first)
Nobody ships a Java 1.4 compiler any more, so the build compiles with a modern
javac --release 8, refuses classes that reference post-1.4 APIs, and rewrites
the class file version to 48. The sources therefore stay inside Java 1.4: no
string + (it compiles to StringBuilder), no autoboxing, no class literals,
no for-each over collections, no inner classes. test_patch.py is the proof:
it uses the game's JVM, working directory and launch flags, against the sidecar
and then against a dead port to show the fallback. Commit patch/prebuilt/
with the source change; a unit test fails if the two drift apart.
eval/cases.tsv is a set of labelled player lines, some from real play
sessions. The option descriptions in da_schema.py were tuned against it, so
it is a regression check, not a benchmark. Jev currently gets all 84, at about
200 ms median per request; the full round trip as the game sees it is typically
130 to 500 ms, in the same range as the game's own 300 ms input polling.
When a line is misread, the monitor shows which question went wrong; the fix is
usually a clearer option description. When it is unclear where a phrase
belongs, the game's grammar is the authority: tools/show_rules.py will show,
for instance, that "stop it" is a disagree idiom there, not a pacify.
Not done #
- The
Miscact and its beat-specific parameters, and drink orders, are not mapped. Lines that would have used them deflect or land on a nearby act. - Gestures (hug, comfort, kiss, picking things up) never went through the text parser and are untouched.
Credits #
Façade is by Michael Mateas and Andrew Stern (Procedural Arts). Reading the game's back end was only practical thanks to the FacadeResearch decompilation.
Code in this repository is MIT licensed; see LICENSE.