ghostyles/00-template.jsjavascript
/**
* ==Ghostyle==
* @name Template
* @version 2.0.0
* @author vecna
* @release_date 2026-06-29
* @description Canonical template for Ghostmaxxing plugins: minimal example with 2D + UV callbacks.
* ==/Ghostyle==
*/
/**
* This file is intentionally heavily commented. Its purpose is not the visual
* effect itself, but to demonstrate how to build a plugin compatible with the
* unified model (onInit, onDraw, paintUV, and onClear).
*
* @module ghostyles/00-template
*/
const G = window.gstmxx || window.Ghostmaxxing || window.Ghostati;
/**
* Initialization callback called when the plugin is loaded or activated.
* Used to set up any initial state, perform one-time calculations, or log initialization.
*
* @returns {string|void} An optional initialization message.
*/
export function onInit() {
if (G && typeof G.log === 'function') {
G.log('Template plugin initialized.', 'Template');
}
return 'Template initialized';
}
/**
* 2D callback called during the face-api rendering tick.
*
* For a teaching template, we want a minimal visual action: a thin circle
* centered on the nose. We avoid complex shapes to keep the template easy
* to understand and copy.
*
* @param {CanvasRenderingContext2D} ctx - The overlay canvas 2D rendering context.
* @param {object} landmarks - The face-api landmarks object containing facial features.
*/
export function onDraw(ctx, landmarks) {
// Nose landmarks contain 9 points. The centroid is the geometric average
// of the area, which is robust even if the face rotates slightly.
const nose = landmarks.getNose();
const c = G.avgPoint(nose);
// The radius scales with the vertical length of the nose to adapt to
// larger/smaller faces without using hardcoded absolute pixel values.
const radius = Math.max(8, G.distance(nose[0], nose[6]) * 0.18);
// Draw a subtle thin circle so the template doesn't dominate the frame.
ctx.save();
ctx.beginPath();
ctx.arc(c.x, c.y, radius, 0, Math.PI * 2);
ctx.lineWidth = 1.6;
ctx.strokeStyle = 'rgba(235, 245, 255, 0.85)';
ctx.stroke();
ctx.restore();
}
/**
* UV callback called during the MediaPipe rendering tick.
*
* This draws a semi-transparent band in the forehead area of the UV texture.
* It demonstrates that paintUV does not draw directly on the screen/video space,
* but on the canonical face texture which is then warped onto the 3D face mesh.
*
* @param {CanvasRenderingContext2D} ctx - The UV texture canvas 2D context.
*/
export function paintUV(ctx) {
// It is not necessary to know all 478 indices for a basic example: we draw
// a band across the upper section of the UV texture, where the forehead is located.
const w = ctx.canvas.width;
const h = ctx.canvas.height;
// Use a highly readable yet semi-transparent color to see the overlay effect.
ctx.save();
ctx.fillStyle = 'rgba(90, 210, 235, 0.22)';
// Horizontal band with side margins to avoid drawing on the very edge of the texture.
const x = w * 0.18;
const y = h * 0.08;
const bw = w * 0.64;
const bh = h * 0.16;
ctx.fillRect(x, y, bw, bh);
// A subtle border helps outline the shape's boundaries on the UV map.
ctx.lineWidth = Math.max(1, w * 0.006);
ctx.strokeStyle = 'rgba(225, 250, 255, 0.4)';
ctx.strokeRect(x, y, bw, bh);
ctx.restore();
}
/**
* Cleanup callback called when the plugin is unloaded or deactivated.
* Used to clean up any custom event listeners, timers, or custom drawing state.
*
* @param {CanvasRenderingContext2D} ctx - The overlay canvas 2D rendering context.
*/
export function onClear(ctx) {
if (G && typeof G.log === 'function') {
G.log('Template plugin cleared.', 'Template');
}
}