Install
$ agentstack add skill-citiesskylinesmodding-agents-plugins-gameface ✓ scanned · ✓ verified, works with Claude Code, Cursor, and more.
Security review
✓ PassedNo issues found. Passed automated security review. · v0.1.0 How review works →
- ✓ Prompt-injection patterns
- ✓ Secret / credential exfiltration
- ✓ Dangerous shell & filesystem operations
- ✓ Untrusted network calls
- ✓ Known-malicious package signatures
What it can access
- ● Network access Used
- ✓ Filesystem access No
- ✓ Shell / process execution No
- ✓ Environment & secrets No
- ✓ Dynamic code execution No
From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.
Verified badge
Passed review? Show it. Paste this badge into your README, it links to the public security report.
Reliability & compatibility
Declared compatibility
Compatibility is declared by the source manifest. End-to-end runtime verification is coming, see below.
We're building live execution health for every listing: tool-call success rate, median latency, uptime, and last-checked timestamps, measured, not self-reported. It isn't live yet, so we don't show numbers we can't stand behind.
How agent discovery & health will work →About
Gameface
Gameface renders game UI from HTML/CSS/JS. It is powered by Cohtml (the HTML engine) and Renoir (the rendering engine), proprietary technology that is not WebKit, Chromium, or Gecko, and not a WebView. JavaScript runs on V8. Gameface implements a deliberate subset of HTML5/CSS3 chosen for game-UI performance: an unsupported HTML element is not an error, it lays out as a generic unstyled flex box with no semantics, and unsupported CSS is silently ignored. (The older Coherent UI product was Chromium-based; search results about it do not describe Gameface.) This skill was written against docs v3.0.3.1 and verified against Cities: Skylines II (CS2), the worked example throughout.
The map and the territory
The docs are the map; the running game is the territory. Three rules keep them straight:
- The docs describe the latest Gameface only. Support tables carry no "since version" annotations.
A YES in today's docs is an upper bound, not a fact about your game.
- Version-gate every feature claim. A game embeds a Cohtml version frozen at ship time (CS2: 1.64.0.7, while the docs describe 3.0.x).
A feature exists in the game iff the changelog introduced it at or below the game's version. [references/version-gating.md](references/version-gating.md) has the lookup procedure, a baked version-to-feature timeline, and the list of features that never existed at all.
- Probe the territory. Two games on the same Cohtml version can still differ: per-game compatibility flags and embedder choices gate complex-selector styling, WebSockets, localization, text-transform, and more.
When a game is reachable, game_status reports the engine version (the CDP endpoint answers Browser: "Cohtml/x.y.z"), and game_eval settles support questions in one probe: typeof ResizeObserver for APIs, a style round-trip for CSS (el.style.setProperty('gap', '4px') then read it back; the parser rejects unsupported values, so they read back empty). CSS.supports does not exist (the CSS global is only unit factories like CSS.px). Page JS has no version global; feature-detect, never UA-sniff.
A support claim is settled only once it is version-gated, and probed when a game is connected.
Probing produces hard-won facts the docs lack: an undocumented quirk, a version boundary, a per-game flag setting. Store engine facts you discovered yourself in your auto memory, tagged with the game and Cohtml version they were verified on; they outlive the session and are expensive to rediscover.
Not a browser: the facts that bite first
Layout and styling:
- Every element defaults to
display: flex(column direction) withbox-sizing: border-box.
block and inline are simulated with flex. flex-shrink defaults to 0. ` and ` default to flex row.
min-width/min-height: automeans 0.
Percentages on absolutely-positioned elements resolve against the direct parent, not the first positioned ancestor. `` is 100vw by 100vh.
- Flexbox is the only layout system.
CSS Grid, floats, table layout, list markers, position: sticky, and display: inline/inline-block/contents have never existed in any version.
- Native form controls stop at text/password inputs, textarea, and button-as-styled-element.
`, radio, checkbox, range, , and / rendering come from official polyfills; tables, lists, /, /, and ` lay out as plain boxes. Overflow scrolls but draws no scrollbar; scrollbars are built or polyfilled.
- Stylesheet combinators (
>,+,~, descendant space) only match when the game enables complex-selector styling (the per-gameEnableComplexCSSSelectorsStylingflag): probe before relying on them.
:not(), ::placeholder, and :nth-of-type() are unsupported; ::before/::after exist since 1.19.
user-selectdefaults tonone; text selection is opt-in.- CSS variables work, except inside
@keyframesand asvar()fallback values.
calc() cannot mix % with other units. currentcolor does not exist. Variables inside color functions are the root cause of most Tailwind utility failures.
- Media types do not exist (
screenis implied); write feature-only queries:@media (min-width: 1280px).
Timing (the game loop is the UI clock):
- Timers, requestAnimationFrame, CSS animations, and event dispatch tick inside the game's per-frame
View::Advancecall.
Effective rAF rate equals the game frame rate; a game that stops advancing the view freezes page JS entirely.
- Layout runs once per frame.
Geometry and styles read from JS are one frame stale; a node measured in the same tick it was inserted reports 0x0; getComputedStyle settles 2-3 frames after a change. Idiom: wrap reads in requestAnimationFrame, nested twice for computed styles.
- Hover state and
mouseenter/over/leave/outonly update when the game forwards mouse-move or scroll input.
JS and DOM:
- The global
engine(defined by cohtml.js; gate startup onengine.whenReady) is the only JS-to-game bridge:engine.call(name, ...)returns a Promise from the single C++ handler,engine.trigger/engine.onare N-handler events, and models flow throughengine.createJSModel/engine.updateWholeModel/engine.synchronizeModelsplusdata-bind-*attributes. fetch(),IntersectionObserver, Web Workers, iframes, `,, andcontenteditable` have never existed.
XHR and localStorage exist (sessionStorage is absent), served by the game's resource layer (coui:// scheme) rather than a network stack. WebSocket exists only when the game wired a socket transport. V8 is 9.4 (ES2021) since Cohtml 1.26, with no later bump recorded.
event.target/currentTargetare null once the dispatch call stack unwinds.
DOMContentLoaded exists only since 1.27; load fires after fonts load.
document.evaluate(XPath),createTreeWalker, andinnerTextdo not exist, anddocument.titleis undefined (verified on CS2).
Find elements by scanning querySelectorAll results and filtering on textContent.
HTMLElement.click()does not exist, andPointerEvent/InputEventconstructors are missing.
Simulate input by dispatching bubbling MouseEvent, KeyboardEvent, and Event('input') events.
Looking things up
Docs base: https://docs.coherent-labs.com/cpp-gameface/. Summarizing fetch tools (WebFetch and similar) fail on this site: every page front-loads hundreds of KB of minified navigation, so summarizers never reach the content. Fetch pages with the shipped extractor instead (paths relative to this skill's directory):
node scripts/fetch-doc.mjs # page content as markdown, tables intact
node scripts/fetch-doc.mjs sitemap # list every page URL of the docs site
Fallback without node: curl -s , then read the HTML between ``.
Key pages (paths under the base):
| Page | Path | | ----------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------- | | Differences to traditional browsers (prose, the quirks) | what_is_gfp/htmlfeaturesupport/ | | Supported-features hub | content_development/supported_features_tables/ | | HTML / CSS properties / CSS selectors / JS events / canvas / SVG tables | content_development/supported_features_tables/ + htmlelements/, cssproperties/, cssselectors/, jsevents/, canvassupport/, svgsupport/ | | Feature changelog (all versions, one page) | changelog/feature/ | | LTS changelog (post-branch fixes) | changelog/lts/ | | Content development (guides, tooling) | content_development/ | | UI scripting (engine API, data binding) | integration/ui_scripting/ |
Reading the support tables:
- Find the exact row.
JS events repeat per specification: match name AND specification (load under DOM Level 3 is YES; load under XHR is not).
- An empty status cell means unsupported.
The tables never write NO, except the canvas page, which does.
- PARTIAL, or any Notes cell, enumerates the exact supported subset; read it verbatim.
- CSS rows carry an Animatable column (YES / NO / DISCRETE / empty).
Selector rows use footnotes: * needs the complex-selectors flag, ** warns about structural pseudo-class performance.
- The "Differences to traditional browsers" prose page holds behavioral quirks the tables omit; check it when a supported feature still behaves oddly.
Going deeper
- Version-gating a feature, "does the game have X", what changed between versions, the CS2 feature ceiling: [references/version-gating.md](references/version-gating.md).
- Layout modes, default-style deltas, media queries, UI scaling, fonts, text, emoji, filters, animation, SVG/GIF/Lottie/video: [references/layout-styling-text.md](references/layout-styling-text.md).
- The
engineAPI surface,data-bind-*vocabulary, model lifecycle, mock game data, simulating input, DOM/JS quirks: [references/scripting-data-binding.md](references/scripting-data-binding.md). - UI is slow, janky, or memory-hungry; the frame pipeline; profiling with DevTools: [references/performance.md](references/performance.md).
- Dev workflow: the Player, DevTools panels, React/Preact/Solid/Svelte/Tailwind, polyfills, linters, TypeScript typings: [references/tooling-workflow.md](references/tooling-workflow.md).
Source & license
This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.
- Author: CitiesSkylinesModding
- Source: CitiesSkylinesModding/agents-plugins
- License: MIT
Install and usage instructions live in the source repository linked above.
Reviews
No reviews yet, be the first.
Write a review
Versions
- v0.1.0 Imported from the upstream source.