Skip to content

Commit 16fbd95

Browse files
authored
Merge pull request #11 from vecna/feat/consent-tools
feat/consent-tools and also includes #10
2 parents aa433e2 + 0b14fe5 commit 16fbd95

35 files changed

Lines changed: 5774 additions & 261 deletions

.github/workflows/ci.yml

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
name: CI
2+
3+
on:
4+
pull_request:
5+
branches:
6+
- main
7+
push:
8+
branches:
9+
- main
10+
11+
jobs:
12+
test:
13+
name: Test
14+
runs-on: ubuntu-latest
15+
permissions:
16+
contents: read
17+
18+
steps:
19+
- name: Check out repository
20+
uses: actions/checkout@v4
21+
22+
- name: Set up Node.js
23+
uses: actions/setup-node@v4
24+
with:
25+
node-version: 20
26+
cache: npm
27+
28+
- name: Install dependencies
29+
run: npm ci
30+
31+
- name: Install Playwright Chromium
32+
run: npx playwright install --with-deps chromium
33+
34+
- name: Run unit tests with coverage
35+
run: npm run test:coverage
36+
37+
- name: Run end-to-end tests
38+
run: npm run test:e2e
39+
40+
update-coverage-badge:
41+
name: Update Coverage Badge
42+
runs-on: ubuntu-latest
43+
needs: test
44+
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
45+
permissions:
46+
contents: write
47+
48+
steps:
49+
- name: Check out repository
50+
uses: actions/checkout@v4
51+
with:
52+
fetch-depth: 0
53+
54+
- name: Set up Node.js
55+
uses: actions/setup-node@v4
56+
with:
57+
node-version: 20
58+
cache: npm
59+
60+
- name: Install dependencies
61+
run: npm ci
62+
63+
- name: Generate coverage report
64+
run: npm run test:coverage
65+
66+
- name: Update README coverage badge
67+
run: npm run update:coverage-badge
68+
69+
- name: Commit updated coverage badge
70+
run: |
71+
if git diff --quiet -- README.md; then
72+
echo "No coverage badge changes to commit"
73+
exit 0
74+
fi
75+
git config user.name "github-actions[bot]"
76+
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
77+
git add README.md
78+
git commit -m "chore(ci): update coverage badge [skip ci]"
79+
git push

README.md

Lines changed: 15 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
# Ghostmaxxing
22
<!-- coverage-badge:start -->
3-
[![Unit Test Coverage](https://img.shields.io/badge/coverage-73.12%25-yellow)](coverage/)
3+
[![Unit Test Coverage](https://img.shields.io/badge/coverage-66.96%25-yellow)](coverage/)
44
<!-- coverage-badge:end -->
5+
[![CI](https://github.com/vecna/ghostmaxxing/actions/workflows/ci.yml/badge.svg)](https://github.com/vecna/ghostmaxxing/actions/workflows/ci.yml)
56

67
[![Docs](https://img.shields.io/badge/docs-JSDoc-blue)](docs/)
78
[![Source](https://img.shields.io/badge/source-GitHub-black)](https://github.com/vecna/ghostati)
@@ -77,11 +78,23 @@ The project is a static web app: there is no production build step required to o
7778
Important local entry points:
7879

7980
- [`index.html`](index.html) — public landing page with links to code, docs, coverage, Ghostyles, project context, and the reporting node.
80-
- [`ghostati.html`](ghostati.html) — main webcam/AR application.
81+
- [`lab.html`](lab.html) — translated main webcam/AR application.
82+
- [`loader.html`](loader.html) — translated internal MP4 loader for repeatable 2D/3D video tests.
83+
- [`ghostati.html`](ghostati.html) — legacy main webcam/AR application entry point.
8184
- [`ghostyles.json`](ghostyles.json) — Ghostyle manifest.
8285
- [`JSDOC_README.md`](JSDOC_README.md) — concise generated-docs overview.
8386
- [`REFERENCES.json`](REFERENCES.json) — curated technical/cultural reference set.
8487

88+
# Localization coverage
89+
90+
Ghostmaxxing currently ships runtime translations for English, Italian, and Portuguese through [`scripts/i18n.js`](scripts/i18n.js). The translated browser pages are:
91+
92+
- [`index.html`](index.html) — homepage and public navigation.
93+
- [`lab.html`](lab.html) — main camera lab, settings, drawers, controls, status labels, and runtime logs.
94+
- [`loader.html`](loader.html) — internal MP4 video loader, language switcher, static interface labels, model status, and loader activity logs.
95+
96+
Other static pages such as [`about.html`](about.html), [`report.html`](report.html), workshop/tutorial pages, generated docs, coverage, and reference pages are still English-baseline unless they add `data-i18n*` bindings and catalog keys.
97+
8598
Runtime external dependencies visible from the HTML/config layer:
8699

87100
- [Google Fonts](https://fonts.googleapis.com) / [Google Fonts static assets](https://fonts.gstatic.com)
@@ -304,36 +317,6 @@ Current reference links:
304317

305318
```text
306319
small patch
307-
clear test
308-
stable plugin API
309-
documented behavior
310-
```
311-
312-
Good contributions include:
313-
314-
- new Ghostyles with clear metadata and reproducible test notes;
315-
- tighter unit coverage around renamed/refactored functions;
316-
- e2e scenarios for detection, baseline saving, overlay switching, and match-state transitions;
317-
- CDN self-hosting options;
318-
- clearer model-loading failure states;
319-
- accessibility and mobile UI improvements;
320-
- better documentation for `faceapi` vs `mediapipe` Ghostyle engines;
321-
- additions to [`REFERENCES.json`](REFERENCES.json) following [`PROMPT-REFERENCES-UPDATE.txt`](PROMPT-REFERENCES-UPDATE.txt).
322-
323-
Please keep claims narrow and technical: say which model, browser, lighting, camera, and threshold produced which result.
324-
325-
326-
327-
328-
329-
330-
331-
332-
333-
**Last commit:** `85f9100` – internationalization added, three languages
334-
335-
336-
337320
338321
339322

about.html

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -207,6 +207,25 @@ <h3>A ban that keeps getting reopened.</h3>
207207
</p>
208208
</section>
209209

210+
<section class="content-section" id="internal-tools">
211+
<h2>Internal tools &amp; diagnostics.</h2>
212+
<p>
213+
To support deeper analysis, model testing, and pattern sharing, Ghostmaxxing includes experimental diagnostic tools. These utilities run entirely locally in the browser to examine recorded behavior and transfer designs.
214+
</p>
215+
<h3>Video Loader</h3>
216+
<p>
217+
The <a href="loader.html">Video Loader</a> allows users to import local MP4 video files to run against our on-device 2D and 3D face-detection engines. This allows for precise frame-by-frame analysis, face extraction, database recording, and signature comparison without requiring a live camera feed. It is designed to evaluate recorded workshop outcomes or diagnostic video tracks under stable, repeatable conditions.
218+
</p>
219+
<h3>Ghostyle Transfer</h3>
220+
<p>
221+
The <a href="ghostyle-transfer.html">Ghostyle Transfer</a> tool extracts painted makeup patterns from a workshop before/after image pair and attaches them to a new target face. When the local face engine detects matching landmarks, the tool aligns the pattern using a 3D face mesh. Otherwise, it scales the pattern using manual bounding boxes, letting researchers visual-test camouflage layouts on different facial structures before physical application.
222+
</p>
223+
<h3>Latent Space Visualizer</h3>
224+
<p>
225+
The <a href="realtime.html">Latent Space Visualizer</a> is a real-time visual debugger for analyzing face descriptor drift. By recording a baseline face signature via webcam, it tracks facial embedding alterations and showcases biometric distance thresholds alongside a 128-dimensional descriptor equalizer. It is used to study exactly how specific facial movements, lighting shifts, and makeup strokes skew facial signatures.
226+
</p>
227+
</section>
228+
210229
<section class="content-section" id="resources">
211230
<h2>Read, test, report.</h2>
212231
<p>
@@ -219,6 +238,18 @@ <h2>Read, test, report.</h2>
219238
<h3>Open the lab</h3>
220239
<p>Run local browser tests and watch a recognition pipeline react to your face in real time.</p>
221240
</a>
241+
<a class="resource-card" href="loader.html">
242+
<h3>Video Loader</h3>
243+
<p>Run pre-recorded MP4 video tests to extract and compare face signatures frame-by-frame.</p>
244+
</a>
245+
<a class="resource-card" href="ghostyle-transfer.html">
246+
<h3>Ghostyle Transfer</h3>
247+
<p>Extract makeup patterns from before/after images and project them onto new target faces.</p>
248+
</a>
249+
<a class="resource-card" href="realtime.html">
250+
<h3>Latent Space Visualizer</h3>
251+
<p>Debug biometric distance thresholds and visualize face descriptor drift in real time.</p>
252+
</a>
222253
<a class="resource-card" href="/references/">
223254
<h3>Research references</h3>
224255
<p>Trace the artistic, technical, and activist lineage of face-obfuscation work.</p>
@@ -243,6 +274,7 @@ <h2>On this page</h2>
243274
<a href="#origin">Origin</a>
244275
<a href="#research-question">Research question</a>
245276
<a href="#leaks">Why leaking matters</a>
277+
<a href="#internal-tools">Internal tools</a>
246278
<a href="#resources">Resources</a>
247279
</nav>
248280
</aside>

ghostyle-transfer.html

Lines changed: 176 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,176 @@
1+
<!doctype html>
2+
<html lang="en">
3+
4+
<head>
5+
<meta charset="utf-8" />
6+
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover" />
7+
<title>Ghostyle Transfer — Ghostmaxxing</title>
8+
<meta name="generator" content="Hand-coded with rage against the algorithm" />
9+
<meta name="description"
10+
content="Extract a Ghostyle from a before/after workshop pair and transfer it to another face locally in the browser." />
11+
<meta property="og:title" content="Ghostyle Transfer - Ghostmaxxing" />
12+
<meta property="og:description" content="Transfer adversarial makeup patterns between faces in a local browser lab." />
13+
<meta property="og:type" content="website" />
14+
<meta property="og:image" content="/images/social-card.svg" />
15+
<meta name="twitter:card" content="summary_large_image" />
16+
<meta name="twitter:title" content="Ghostyle Transfer - Ghostmaxxing" />
17+
<meta name="twitter:description" content="Transfer adversarial makeup patterns between faces in a local browser lab." />
18+
<meta name="twitter:image" content="/images/social-card.svg" />
19+
<link rel="icon" type="image/svg+xml" href="/images/ghostmaxxing-favicon.svg" />
20+
<link rel="stylesheet" href="styles/pages.css" />
21+
<link rel="stylesheet" href="styles/content-pages.css" />
22+
<script src="https://cdn.jsdelivr.net/npm/@vladmandic/face-api/dist/face-api.js"></script>
23+
</head>
24+
25+
<body>
26+
<main class="editorial-homepage content-page editorial-homepage--svg-footer-band">
27+
<div class="editorial-homepage__shell">
28+
<header class="editorial-homepage__header">
29+
<a class="wordmark" href="index.html" aria-label="Ghostmaxxing homepage" data-i18n-aria-label="homepage_wordmark_label">
30+
<span class="wordmark__main">
31+
<img class="wordmark__mark" src="images/ghostmaxxing-mark.svg" alt="" aria-hidden="true" />
32+
<span class="wordmark__title">Ghostmaxxing</span>
33+
</span>
34+
<span class="wordmark__subtitle" data-i18n="homepage_subtitle">
35+
A public lab for testing face-recognition camouflage
36+
</span>
37+
</a>
38+
39+
<nav class="editorial-homepage__nav" aria-label="Project navigation" data-i18n-aria-label="prototype_navigation_label">
40+
<a class="navlink" href="/references/" rel="noopener noreferrer" data-i18n="research_references_link">Research References</a>
41+
<a class="navlink" href="about.html" data-i18n="vision_about_link">Vision &amp; About</a>
42+
<a class="cta-inline" href="lab.html" data-i18n-html="open_lab_link_html">Open the lab <span aria-hidden="true"></span></a>
43+
<span class="locale-control">
44+
<span class="locale-control__flag" aria-hidden="true"></span>
45+
<select id="localeSelect" class="locale-select" aria-label="Language" data-i18n-aria-label="language_label"></select>
46+
</span>
47+
</nav>
48+
</header>
49+
50+
<section class="content-page__header-area" aria-labelledby="transfer-title">
51+
<span class="content-page__kicker" data-i18n="transfer_kicker">Lab · Ghostyle transfer</span>
52+
<h1 id="transfer-title" data-i18n="transfer_title">Lift a Ghostyle off one face, wear it on another.</h1>
53+
<p class="content-page__lead" data-i18n="transfer_lead">
54+
Extract the paint from a workshop before / after pair and place it on a target face.
55+
</p>
56+
<p class="content-page__deck" data-i18n="transfer_deck">
57+
Mark the face on each image with a box. The transfer runs entirely on this device, with mesh alignment when the local face engine can detect matching landmarks.
58+
</p>
59+
<span class="tool-status" id="engineBadge">
60+
<span class="tool-status__dot" aria-hidden="true"></span>
61+
<span id="engineText" data-i18n="transfer_engine_loading">Mesh engine: loading…</span>
62+
</span>
63+
</section>
64+
65+
<section class="tool-workbench" aria-label="Ghostyle transfer workbench" data-i18n-aria-label="transfer_workbench_label">
66+
<div class="tool-grid">
67+
<article class="tool-card">
68+
<div class="tool-card__header">
69+
<span class="tool-card__title" data-i18n="transfer_before_title">Before</span>
70+
<span class="tool-card__tag" data-i18n="transfer_before_tag">bare face · optional</span>
71+
</div>
72+
<div class="tool-stage" id="stage-before">
73+
<span class="tool-stage__hint" id="hint-before" data-i18n="transfer_before_hint">Drop or choose the bare workshop face</span>
74+
</div>
75+
<p class="tool-note" id="note-before"></p>
76+
<label class="file-picker">
77+
<input type="file" accept="image/*" data-slot="before" />
78+
<span data-i18n="transfer_choose_image">Choose image</span>
79+
</label>
80+
</article>
81+
82+
<article class="tool-card">
83+
<div class="tool-card__header">
84+
<span class="tool-card__title" data-i18n="transfer_after_title">After</span>
85+
<span class="tool-card__tag" data-i18n="transfer_after_tag">painted face</span>
86+
</div>
87+
<div class="tool-stage" id="stage-after">
88+
<span class="tool-stage__hint" id="hint-after" data-i18n="transfer_after_hint">Drop or choose the painted workshop face</span>
89+
</div>
90+
<p class="tool-note" id="note-after"></p>
91+
<label class="file-picker">
92+
<input type="file" accept="image/*" data-slot="after" />
93+
<span data-i18n="transfer_choose_image">Choose image</span>
94+
</label>
95+
</article>
96+
97+
<article class="tool-card">
98+
<div class="tool-card__header">
99+
<span class="tool-card__title" data-i18n="transfer_target_title">Target</span>
100+
<span class="tool-card__tag" data-i18n="transfer_target_tag">receives the paint</span>
101+
</div>
102+
<div class="tool-stage" id="stage-target">
103+
<span class="tool-stage__hint" id="hint-target" data-i18n="transfer_target_hint">Drop or choose the target face / painting</span>
104+
</div>
105+
<p class="tool-note" id="note-target"></p>
106+
<label class="file-picker">
107+
<input type="file" accept="image/*" data-slot="target" />
108+
<span data-i18n="transfer_choose_image">Choose image</span>
109+
</label>
110+
</article>
111+
</div>
112+
113+
<div class="tool-controls">
114+
<div class="tool-field">
115+
<label class="tool-label" for="thr"><span data-i18n="transfer_sensitivity_label">Paint sensitivity</span> <output id="v-thr">28</output></label>
116+
<input type="range" id="thr" min="6" max="90" value="28" />
117+
</div>
118+
<div class="tool-field">
119+
<label class="tool-label" for="fth"><span data-i18n="transfer_feather_label">Edge feather</span> <output id="v-fth">3</output></label>
120+
<input type="range" id="fth" min="0" max="12" value="3" />
121+
</div>
122+
<div class="tool-field">
123+
<label class="tool-label" for="op"><span data-i18n="transfer_opacity_label">Opacity</span> <output id="v-op">92</output>%</label>
124+
<input type="range" id="op" min="20" max="100" value="92" />
125+
</div>
126+
<div class="tool-field">
127+
<label class="tool-label" for="blend" data-i18n="transfer_blend_label">Blend</label>
128+
<select id="blend">
129+
<option value="multiply" selected data-i18n="transfer_blend_multiply">Multiply (paints onto)</option>
130+
<option value="source-over" data-i18n="transfer_blend_normal">Normal</option>
131+
<option value="soft-light" data-i18n="transfer_blend_soft_light">Soft light</option>
132+
</select>
133+
</div>
134+
<div class="tool-field tool-field--check">
135+
<input type="checkbox" id="useMesh" checked />
136+
<label for="useMesh" data-i18n="transfer_use_mesh_label">Follow features (mesh)</label>
137+
</div>
138+
</div>
139+
140+
<div class="tool-actions">
141+
<button class="btn-primary" id="run" disabled data-i18n="transfer_run_button">Transfer Ghostyle</button>
142+
<button class="link-secondary" id="download" disabled data-i18n="transfer_download_button">Download result</button>
143+
<button class="link-secondary" id="reset" data-i18n="transfer_reset_button">Reset</button>
144+
<span class="tool-message" id="msg" data-i18n="transfer_msg_load_after_target">Load an after and a target, then draw a box on each face.</span>
145+
</div>
146+
147+
<div class="tool-output">
148+
<div class="tool-result" id="result">
149+
<span class="tool-result__empty" data-i18n="transfer_result_empty">The transferred result will appear here.</span>
150+
</div>
151+
<aside class="tool-aside">
152+
<section class="tool-panel">
153+
<h2 data-i18n="transfer_matte_title">Extracted paint</h2>
154+
<canvas id="matte" width="180" height="180"></canvas>
155+
<p id="matteNote" data-i18n="transfer_matte_note">The isolated Ghostyle, in a normalized face frame.</p>
156+
</section>
157+
<section class="tool-panel">
158+
<h2 data-i18n="transfer_mode_title">Mode</h2>
159+
<p id="modeNote" data-i18n="transfer_mode_not_run">Not run yet.</p>
160+
</section>
161+
</aside>
162+
</div>
163+
</section>
164+
165+
<footer class="tool-disclaimer">
166+
<p data-i18n="transfer_disclaimer">
167+
Ghostmaxxing is a research and education tool. Transferring a Ghostyle onto a face is a visualization; it is not evidence that the pattern affects any recognition system. Face detectors are unreliable on stylized artwork. If the mesh cannot find a target face, the tool uses the box you drew.
168+
</p>
169+
</footer>
170+
</div>
171+
</main>
172+
173+
<script type="module" src="scripts/transfer.js"></script>
174+
</body>
175+
176+
</html>

index.html

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,11 @@ <h1 class="hero-copy__title" id="hero-title-a" data-i18n-html="hero_title_html">
5656
<div class="hero-copy__actions">
5757
<a class="btn-primary" href="lab.html" data-i18n-html="open_lab_link_html">Open the lab <span aria-hidden="true"></span></a>
5858
</div>
59+
<div class="hero-copy__meta" aria-label="Internal tools" data-i18n-aria-label="internal_tools_label">
60+
<span><a href="loader.html" data-i18n="video_loader_link">Video Loader</a></span>
61+
<span><a href="ghostyle-transfer.html" data-i18n="ghostyle_transfer_link">Ghostyle Transfer</a></span>
62+
<span><a href="realtime.html" data-i18n="realtime_visualizer_link">Latent Space Visualizer</a></span>
63+
</div>
5964
<div class="hero-copy__meta" aria-label="Explore further" data-i18n-aria-label="explore_further_label">
6065
<span><a href="/docs" data-i18n="technical_documentation_link">Technical documentation</a></span>
6166
<span><a href="https://github.com/vecna/ghostati" data-i18n="code_link">Code</a></span>

0 commit comments

Comments
 (0)