Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
62 changes: 53 additions & 9 deletions ghostati-docs.html
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,44 @@
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Ghòstati - Guida per Sviluppatori</title>
<link rel="stylesheet" href="styles/common.css" />
<link rel="stylesheet" href="styles/ghostati-docs.css" />
<link rel="stylesheet" href="styles/index.css" />

<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "WebPage",
"name": "Ghòstati - Guida per Sviluppatori",
"url": "https://sindacato.nina.watch/ghostati/ghostati-docs.html",
"author": {
"@type": "Organization",
"@id": "https://www.nina.watch/",
"name": "N.I.N.A. - Nè Intelligente Nè Artificiale"
},
"mainEntity": {
"@type": "TechArticle",
"headline": "Sviluppa il tuo Ghostyle",
"datePublished": "2026-04-21T12:00:00+02:00",
"dateModified": "2026-05-02T01:26:00+02:00",
"image": "https://sindacato.nina.watch/ghostati/images/docs-cover.jpg",
"proficiencyLevel": "Intermediate",
"author": {
"@type": "Person",
"@id": "https://me.vecna.eu",
"name": "vecna",
},
"publisher": {
"@id": "https://www.nina.watch/#organization"
},
},
"relatedLink": [
"https://github.com/vecna/ghostati"
]
}
</script>
</head>

<body>
<body class="page-docs">
<div class="page">

<header class="ghost-topbar" id="ghostTopbar">
Expand All @@ -31,12 +64,23 @@

</header>

<h1>Documentazione</h1>
<div class="subtitle">Sviluppa il tuo Ghostyle</div>

<div class="jumbotron">
<h1>Documentazione</h1>
<div class="subtitle">Sviluppa il tuo Ghostyle</div>
</div>

<nav class="toc" aria-label="Table of Contents">
<h3>Indice dei Contenuti</h3>
<ul>
<li><a href="#architettura">Architettura ad Alto Livello</a></li>
<li><a href="#template">Template Didattico</a></li>
<li><a href="#spiegazione">Spiegazione dei Blocchi</a></li>
<li><a href="#diagnostica">Modalità Diagnostica (Test CV Dazzle)</a></li>
</ul>
</nav>

<section>
<h2>Architettura ad Alto Livello</h2>
<h2 id="architettura">Architettura ad Alto Livello</h2>
<p>Il sistema "Ghòstati" utilizza un'<strong>architettura a plugin modulari</strong> pensata per separare il
nucleo di tracciamento facciale dagli effetti visivi (i "Ghostyles").</p>

Expand All @@ -56,7 +100,7 @@ <h2>Architettura ad Alto Livello</h2>
</section>

<section>
<h2>Template Didattico</h2>
<h2 id="template">Template Didattico</h2>
<p>Un plugin Ghòstyle per funzionare deve unicamente esportare un paio di funzioni specifiche (gli "Event Hooks").
Questo snippet di codice rappresenta lo scheletro di base per cominciare a sviluppare.</p>

Expand Down Expand Up @@ -109,7 +153,7 @@ <h2>Template Didattico</h2>
</section>

<section>
<h2>Spiegazione dei Blocchi</h2>
<h2 id="spiegazione">Spiegazione dei Blocchi</h2>

<div class="block-explain">
<strong>1. Metadati Commentati</strong>
Expand Down Expand Up @@ -149,7 +193,7 @@ <h2>Spiegazione dei Blocchi</h2>
</section>

<section>
<h2>Modalità Diagnostica (Test CV Dazzle)</h2>
<h2 id="diagnostica">Modalità Diagnostica (Test CV Dazzle)</h2>
<p>Ghòstati non si limita ad applicare il trucco AR in modo cosmetico, ma include uno strumento di
<strong>Diagnostica e Analisi delle Vulnerabilità</strong> progettato per valutare la reale efficacia del
<em>Ghostyle</em> nel neutralizzare gli algoritmi di Facial Recognition (CV Dazzle).
Expand Down
24 changes: 23 additions & 1 deletion ghostati.html
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,35 @@
<meta property="og:image" content="https://sindacato.nina.watch/ghostati/facerec-transparency.png" />
<meta property="og:site_name" content="NINA" />

<link rel="icon" type="image/png" href="favicon.png" />
<link rel="icon" type="image/x-icon" href="favicon.ico" />
<link rel="sitemap" type="application/xml" title="Sitemap" href="/ghostati/sitemap.xml" />

<meta name="generator" content="Hand-coded with rage against the algorithm" />
<link rel="source" href="https://github.com/vecna/ghostati" />

<link rel="stylesheet" href="styles/ghostati.css" />

<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "SoftwareApplication",
"@id": "https://sindacato.nina.watch/ghostati/ghostati.html#app",
"name": "Ghòstati - Trucco avverso",
"applicationCategory": "BrowserApplication",
"operatingSystem": "Any",
"author": {
"@type": "Organization",
"@id": "https://www.nina.watch/",
"name": "N.I.N.A. - Nè Intelligente Nè Artificiale"
},
"creator": {
"@type": "Person",
"@id": "https://me.vecna.eu",
"name": "vecna"
},
"codeRepository": "https://github.com/vecna/ghostati"
}
</script>
</head>

<body>
Expand Down
Binary file modified images/ghostati-hero.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
95 changes: 94 additions & 1 deletion index.html
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,101 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link rel="apple-touch-icon" href="apple-touch-icon.png" />

<link rel="icon" type="image/png" href="favicon.png" />
<link rel="icon" type="image/x-icon" href="favicon.ico" />
<link rel="sitemap" type="application/xml" title="Sitemap" href="/ghostati/sitemap.xml" />

<script type="application/ld+json">
{
"@context": "https://schema.org",
"@graph":[
{
"@type": "SoftwareApplication",
"@id": "https://sindacato.nina.watch/ghostati/#app",
"name": "Ghòstati",
"description": "Laboratorio digitale per sperimentare trucco anti-riconoscimento biometrico.",
"applicationCategory": "BrowserApplication",
"operatingSystem": "Any",
"offers": {
"@type": "Offer",
"price": "0",
"priceCurrency": "EUR"
},
"author": {
"@id": "https://www.nina.watch/#organization"
},
"codeRepository": "https://github.com/vecna/ghostati",
"softwareHelp": "https://sindacato.nina.watch/ghostati/ghostati-docs.html",
"featureList": [
"Riconoscimento facciale offline nel browser",
"Test di efficacia avversariale in tempo reale",
"Supporto mobile e desktop"
]
},
{
"@type": "Organization",
"@id": "https://www.nina.watch/#organization",
"name": "N.I.N.A. - Nè Intelligente Nè Artificiale",
"url": "https://www.nina.watch",
"description": "Iniziativa critica ed esplorazione pratica delle tecnologie di riconoscimento facciale e forme di resistenza.",
"sameAs": [
"https://vecna.eu/",
"https://github.com/vecna"
]
},
{
"@type": "Event",
"@id": "https://supporta.nina.watch/NINA/Festival/8/",
"name": "Ghostati. Fashion-tech e protezione dati biometrici",
"startDate": "2026-05-09T16:00:00+02:00",
"eventAttendanceMode": "https://schema.org/OfflineEventAttendanceMode",
"eventStatus": "https://schema.org/EventScheduled",
"location": {
"@type": "Place",
"name": "Rob de Matt",
"address": {
"@type": "PostalAddress",
"streetAddress": "Via Enrico Annibale Butti, 18",
"addressLocality": "Milano",
"addressCountry": "IT"
}
},
"organizer": {
"@id": "https://www.nina.watch/#organization"
}
}, {
"@type": "Event",
"@id": "https://supporta.nina.watch/NINA/roma/26/",
"name": "Workshop Roma - NINA Festival",
"startDate": "2026-05-10T16:00:00+02:00",
"url": "https://supporta.nina.watch/NINA/roma/26/",
"eventAttendanceMode": "https://schema.org/OfflineEventAttendanceMode",
"eventStatus": "https://schema.org/EventScheduled",
"location": {
"@type": "Place",
"name": "L863",
"address": {
"@type": "PostalAddress",
"streetAddress": "Via Giovanni Ansaldo 3A/3B",
"addressLocality": "Roma",
"addressCountry": "IT"
}
},
"organizer": {
"@id": "https://www.nina.watch/#organization"
}
}, {
"@type": "Service",
"@id": "https://raccontaci.nina.watch/#/submission?context=10c78596-3ea0-4867-b2fb-21fdb8e3f40c",
"url": "https://raccontaci.nina.watch/#/submission?context=10c78596-3ea0-4867-b2fb-21fdb8e3f40c",
"name": "Piattaforma di Whistleblowing",
"serviceType": "Whistleblowing Platform",
"provider": {
"@id": "https://www.nina.watch/#organization"
}
}]
}
</script>

</head>

<body>
Expand Down
11 changes: 10 additions & 1 deletion scripts/ghostati-mobile-ui.js
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,11 @@ document.addEventListener('DOMContentLoaded', () => {
return el.closest('.scrollable') || el.closest('#settingsDrawer');
};

/* [SYSTEM API: document.addEventListener('touchstart'/'touchend')]
* Funzionamento: API nativa per intercettare il tocco delle dita sui display touch.
* Parametri: 'touchstart', callback(e), { passive: true } (migliora le performance dicendo al browser che non useremo preventDefault).
* Feature: Registra l'inizio e la fine di uno swipe verticale per attivare la pulizia dei log e dell'overlay (gesture UI).
*/
document.addEventListener('touchstart', (e) => {
if (isScrollableElement(e.target)) return;
touchStartY = e.changedTouches[0].screenY;
Expand All @@ -71,7 +76,11 @@ document.addEventListener('DOMContentLoaded', () => {
handleGesture();
}, { passive: true });

// Scroll wheel for desktop
/* [SYSTEM API: document.addEventListener('wheel')]
* Funzionamento: Intercetta la rotellina del mouse o il trackpad su desktop.
* Parametri: 'wheel', callback(e), { passive: true }.
* Feature: Replica il comportamento dello swipe (clear overlay e log) anche per gli utenti desktop tramite wheel/scroll.
*/
document.addEventListener('wheel', (e) => {
if (isScrollableElement(e.target)) return;
// Debounce or just trigger on any significant scroll
Expand Down
38 changes: 38 additions & 0 deletions scripts/ghostati.js
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,11 @@ let lastCompositedCanvas = null;

function loadDb() {
try {
/* [SYSTEM API: localStorage]
* Funzionamento: Storage persistente chiave-valore sincrono nativo del browser.
* Parametri: getItem(key) per leggere stringhe.
* Feature: Usato per caricare il DB locale dei volti salvati in precedenza.
*/
const raw = localStorage.getItem(STORAGE_KEY);
if (!raw) return { nextId: 0, faces: [] };
const parsed = JSON.parse(raw);
Expand All @@ -108,6 +113,11 @@ function loadDb() {
}

function persistDb() {
/* [SYSTEM API: localStorage]
* Funzionamento: Scrittura persistente sincrona nel browser.
* Parametri: setItem(key, value)
* Feature: Usato per salvare il DB aggiornato con le nuove feature biometriche, serializzato in JSON.
*/
localStorage.setItem(STORAGE_KEY, JSON.stringify(db));
renderDbStats();
}
Expand Down Expand Up @@ -609,6 +619,11 @@ function effectLoop(ts = 0) {
lastEffectRun = ts;
runEffectPass();
}
/* [SYSTEM API: requestAnimationFrame]
* Funzionamento: Richiede al browser di chiamare una funzione prima del prossimo repaint.
* Parametri: callback(timestamp)
* Feature: Crea un loop di rendering efficiente per le maschere AR, limitando i calcoli superflui e sincronizzandosi col display.
*/
effectLoopHandle = requestAnimationFrame(effectLoop);
}

Expand All @@ -618,6 +633,11 @@ function startEffectLoop() {
}

function stopEffectLoop() {
/* [SYSTEM API: cancelAnimationFrame]
* Funzionamento: Annulla una richiesta di animazione precedentemente schedulata.
* Parametri: handle (ID numerico restituito da rAF)
* Feature: Blocca il loop di rendering AR quando disattiviamo gli effetti.
*/
if (effectLoopHandle) cancelAnimationFrame(effectLoopHandle);
effectLoopHandle = null;
effectInferenceInFlight = false;
Expand Down Expand Up @@ -738,6 +758,11 @@ async function findFace() {
}

async function startCamera() {
/* [SYSTEM API: navigator.mediaDevices.getUserMedia]
* Funzionamento: Richiede all'utente il permesso di accedere alla webcam/microfono. Restituisce un MediaStream.
* Parametri: object { video: { constraints }, audio: boolean }
* Feature: Accediamo al feed video ad alta risoluzione, specificando il 'facingMode' (user/environment) per il selfie-mode.
*/
const stream = await navigator.mediaDevices.getUserMedia({
video: {
width: { ideal: 1920 },
Expand Down Expand Up @@ -906,9 +931,17 @@ async function init() {

ctx.fillText(logText, exportCanvas.width / 2, exportCanvas.height - footerHeight / 2);

/* [SYSTEM API: HTMLCanvasElement.toBlob]
* Funzionamento: Esporta asincronamente il contenuto grafico del canvas in un Blob raw.
*/
exportCanvas.toBlob(blob => {
const file = new File([blob], "ghostati-makeup.png", { type: "image/png" });
const attemptShare = () => {
/* [SYSTEM API: navigator.share]
* Funzionamento: Invoca il menu di condivisione nativo dell'OS mobile/desktop (Web Share API).
* Parametri: object { title, text, files }
* Feature: Fallback essenziale su mobile dove clipboard.write(Image) è bloccato o difettoso per motivi di sicurezza/supporto.
*/
if (navigator.share) {
navigator.share({
title: 'Ghostati Makeup',
Expand All @@ -921,6 +954,11 @@ async function init() {
}
};

/* [SYSTEM API: navigator.clipboard.write]
* Funzionamento: API asincrona per scrivere dati arbitrari nella clipboard di sistema. Richiede contesto sicuro (HTTPS/localhost) e interazione utente.
* Parametri: array di ClipboardItem
* Feature: Usata per permettere all'utente di incollare l'immagine PNG esportata direttamente su Telegram/WhatsApp o software di editing.
*/
if (navigator.clipboard && navigator.clipboard.write) {
try {
navigator.clipboard.write([new ClipboardItem({ 'image/png': blob })])
Expand Down
6 changes: 6 additions & 0 deletions scripts/index-effect.js
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,12 @@ function interactionTargetsTopbar(target) {
return target instanceof Element && Boolean(target.closest('#ghostTopbar'));
}

/* [SYSTEM API: window.addEventListener('pointermove' / 'pointerdown')]
* Funzionamento: API universale che incapsula eventi del mouse, touch e pen/stylus.
* Parametri: evento (es: 'pointermove'), callback(e).
* Feature: Usata per intercettare il movimento dell'utente sull'home page per cancellare l'overlay nero (effetto gratta e vinci),
* ignorando l'interazione se avviene sopra la topbar.
*/
window.addEventListener('pointermove', (event) => {
if (interactionTargetsTopbar(event.target)) {
return;
Expand Down
Loading