File size: 18,220 Bytes
391f3fa 6c9cc0c aaab576 6c9cc0c aaab576 6c9cc0c aaab576 6c9cc0c aaab576 6c9cc0c aaab576 6c9cc0c aaab576 6c9cc0c aaab576 6c9cc0c aaab576 6c9cc0c aaab576 6c9cc0c aaab576 6c9cc0c aaab576 6c9cc0c aaab576 6c9cc0c aaab576 6c9cc0c aaab576 6c9cc0c aaab576 6c9cc0c aaab576 6c9cc0c aaab576 6c9cc0c aaab576 6c9cc0c 391f3fa | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 | <!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="description" content="Prepare a clear portrait reference for LiveFaceSwap AI real-time face swap. Fix orientation, position a square crop, and export a clean RGB PNG locally in your browser." />
<meta name="robots" content="index,follow" />
<link rel="canonical" href="https://huggingface.co/spaces/chris-fox/livefaceswap-reference-prep" />
<title>LiveFaceSwap AI Face Swap Reference Image Prep</title>
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "SoftwareApplication",
"name": "LiveFaceSwap AI Face Swap Reference Image Prep",
"applicationCategory": "MultimediaApplication",
"operatingSystem": "Any modern web browser",
"description": "A browser-based utility that prepares authorized portrait reference images for LiveFaceSwap AI real-time face swap workflows.",
"url": "https://huggingface.co/spaces/chris-fox/livefaceswap-reference-prep",
"isAccessibleForFree": true,
"license": "https://opensource.org/license/mit",
"publisher": { "@type": "Organization", "name": "LiveFaceSwap AI", "url": "https://livefaceswap.ai/" },
"codeRepository": "https://github.com/LiveFaceSwapAI/livefaceswap-reference-prep"
}
</script>
<style>
:root { color-scheme: dark; font-family: Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; background: #080d1c; color: #f4f7ff; }
* { box-sizing: border-box; }
html { scroll-behavior: smooth; }
body { margin: 0; min-height: 100vh; background: radial-gradient(circle at 50% -10%, #273c82 0, #111936 34%, #080d1c 68%); }
main { width: min(1080px, calc(100% - 32px)); margin: 0 auto; padding: 42px 0 72px; }
a { color: #a9c3ff; text-underline-offset: 3px; }
p, li { color: #cbd5f2; line-height: 1.68; }
h1, h2, h3 { letter-spacing: -0.025em; }
h1 { margin: 12px 0 16px; max-width: 880px; font-size: clamp(2.35rem, 6vw, 4.6rem); line-height: 0.98; }
h2 { margin: 0 0 12px; font-size: clamp(1.55rem, 3vw, 2.15rem); }
h3 { margin: 0 0 6px; font-size: 1.05rem; }
.eyebrow { display: inline-flex; padding: 7px 11px; border: 1px solid #5369a4; border-radius: 999px; color: #cbd7ff; background: rgba(64, 87, 157, 0.22); font-size: 0.82rem; font-weight: 750; letter-spacing: 0.04em; text-transform: uppercase; }
.lede { max-width: 790px; margin: 0; font-size: clamp(1rem, 2vw, 1.2rem); }
.hero-links, .project-links { display: flex; flex-wrap: wrap; gap: 12px; margin-top: 22px; }
.button-link { display: inline-flex; align-items: center; justify-content: center; min-height: 42px; padding: 10px 15px; border: 1px solid #6881c3; border-radius: 11px; color: #eef3ff; background: #16244d; font-weight: 750; text-decoration: none; }
.button-link.primary { border-color: #88a8ff; background: #85a7ff; color: #07122d; }
.tool { display: grid; grid-template-columns: minmax(250px, 0.8fr) minmax(300px, 1.2fr); gap: 24px; margin-top: 36px; padding: 24px; border: 1px solid #3d518b; border-radius: 22px; background: rgba(12, 20, 48, 0.91); box-shadow: 0 26px 90px rgba(0, 0, 0, 0.32); }
.controls { display: grid; align-content: start; gap: 17px; }
label { display: grid; gap: 8px; color: #f4f7ff; font-weight: 680; }
input, select, button { width: 100%; font: inherit; }
input[type="file"], select { border: 1px solid #536aa8; border-radius: 11px; padding: 11px; background: #101936; color: inherit; }
input[type="range"] { accent-color: #89a9ff; }
.value { color: #aebfe9; font-weight: 520; }
button { min-height: 44px; border: 0; border-radius: 11px; padding: 11px 15px; background: #89a9ff; color: #07112b; font-weight: 820; cursor: pointer; }
button.secondary { border: 1px solid #536aa8; background: #18264e; color: #eef3ff; }
button:disabled { cursor: not-allowed; opacity: 0.45; }
.preview { min-height: 430px; display: grid; place-items: center; border: 1px dashed #5268a3; border-radius: 16px; overflow: hidden; background: #060a15; }
canvas { display: block; width: 100%; height: auto; max-height: 620px; object-fit: contain; }
.placeholder { max-width: 34ch; padding: 32px; text-align: center; color: #93a4d0; line-height: 1.55; }
.privacy { margin: 16px 2px 0; font-size: 0.91rem; }
.section { margin-top: 64px; }
.section-intro { max-width: 760px; margin: 0; }
.workflow { display: grid; grid-template-columns: repeat(4, 1fr); gap: 14px; margin-top: 22px; }
.card { padding: 19px; border: 1px solid #34477b; border-radius: 16px; background: rgba(18, 28, 61, 0.76); }
.step { width: 32px; height: 32px; display: grid; place-items: center; margin-bottom: 16px; border-radius: 9px; background: #87a7ff; color: #07122c; font-weight: 850; }
.card p { margin: 0; }
.benefits { display: grid; grid-template-columns: repeat(3, 1fr); gap: 14px; margin-top: 22px; }
.details { display: grid; grid-template-columns: 0.8fr 1.2fr; gap: 24px; align-items: start; }
.details ul { margin: 12px 0 0; padding-left: 21px; }
.specs { margin: 0; overflow: hidden; border: 1px solid #34477b; border-radius: 16px; background: rgba(18, 28, 61, 0.76); }
.specs div { display: grid; grid-template-columns: 110px 1fr; gap: 12px; padding: 14px 16px; border-bottom: 1px solid #304272; }
.specs div:last-child { border-bottom: 0; }
dt { color: #f4f7ff; font-weight: 750; }
dd { margin: 0; color: #cbd5f2; line-height: 1.55; }
details { padding: 15px 17px; border: 1px solid #34477b; border-radius: 13px; background: rgba(18, 28, 61, 0.68); }
details + details { margin-top: 10px; }
summary { cursor: pointer; color: #f4f7ff; font-weight: 730; }
details p { margin: 10px 0 0; }
footer { margin-top: 64px; padding-top: 24px; border-top: 1px solid #2e3e6b; }
footer p { max-width: 850px; font-size: 0.92rem; }
@media (max-width: 820px) { .workflow, .benefits { grid-template-columns: 1fr 1fr; } .details { grid-template-columns: 1fr; } }
@media (max-width: 720px) { main { padding-top: 28px; } .tool { grid-template-columns: 1fr; padding: 18px; } .preview { min-height: 310px; } .workflow, .benefits { grid-template-columns: 1fr; } .specs div { grid-template-columns: 88px 1fr; } }
</style>
</head>
<body>
<main>
<header>
<span class="eyebrow">LiveFaceSwap AI · Browser utility</span>
<h1>Prepare a better reference image for real-time face swap</h1>
<p class="lede">Fix portrait orientation, position a square crop around the face, and export a consistent RGB PNG before uploading it as a reference in LiveFaceSwap AI. Processing stays in your browser.</p>
<nav class="hero-links" aria-label="Primary links">
<a class="button-link primary" href="#tool">Prepare a portrait</a>
<a class="button-link" href="https://livefaceswap.ai/?utm_source=huggingface&utm_medium=referral&utm_campaign=reference_prep" target="_blank" rel="noopener">Open LiveFaceSwap AI</a>
</nav>
</header>
<section class="tool" id="tool" aria-labelledby="tool-title">
<div class="controls">
<div><h2 id="tool-title">Reference image prep</h2><p>Upload an authorized portrait or use the synthetic example to test the complete crop-and-export path.</p></div>
<label>Portrait image<input id="file" type="file" accept="image/jpeg,image/png,image/webp" /></label>
<button class="secondary" id="example" type="button">Try synthetic example</button>
<label>Output size<select id="size"><option value="512">512 × 512</option><option value="768">768 × 768</option><option value="1024" selected>1024 × 1024</option></select></label>
<label>Horizontal crop focus <span class="value" id="focus-x-value">0.50</span><input id="focus-x" type="range" min="0" max="1" value="0.5" step="0.01" /></label>
<label>Vertical crop focus <span class="value" id="focus-y-value">0.42</span><input id="focus-y" type="range" min="0" max="1" value="0.42" step="0.01" /></label>
<button id="download" type="button" disabled>Download prepared RGB PNG</button>
</div>
<div class="preview" id="preview" aria-live="polite"><div class="placeholder">Choose a JPG, PNG, or WebP portrait to generate a real square preview. Maximum file size: 20 MB.</div><canvas id="canvas" hidden aria-label="Prepared square portrait preview"></canvas></div>
</section>
<p class="privacy">Your selected image is decoded and processed on this page. It is not sent to an application server.</p>
<section class="section" aria-labelledby="workflow-title">
<h2 id="workflow-title">Where this fits in the face swap workflow</h2>
<p class="section-intro">This page prepares the portrait file; it does not perform identity transformation. The actual real-time face swap happens after you upload the prepared PNG to LiveFaceSwap AI.</p>
<div class="workflow">
<article class="card"><div class="step">1</div><h3>Choose an authorized portrait</h3><p>Use one clear, front-facing face that you own or have permission to process.</p></article>
<article class="card"><div class="step">2</div><h3>Frame the face</h3><p>Correct orientation, choose a size, and adjust the crop focus until the face is centered.</p></article>
<article class="card"><div class="step">3</div><h3>Export a clean PNG</h3><p>Download a square 512, 768, or 1024 pixel RGB PNG with image metadata removed.</p></article>
<article class="card"><div class="step">4</div><h3>Use it in LiveFaceSwap</h3><p>Choose Face Swap, upload the PNG under Reference, turn on your camera, and review the live output.</p></article>
</div>
</section>
<section class="section" aria-labelledby="value-title">
<h2 id="value-title">Why prepare the reference first?</h2>
<div class="benefits">
<article class="card"><h3>Predictable framing</h3><p>Move the crop across a landscape or portrait image instead of losing the intended face to an automatic center crop.</p></article>
<article class="card"><h3>Consistent image format</h3><p>Normalize orientation and produce a square RGB PNG at a known size for the downstream workflow.</p></article>
<article class="card"><h3>Private preprocessing</h3><p>Preview and export locally in your browser before deciding whether to use the image elsewhere.</p></article>
</div>
</section>
<section class="section details" aria-labelledby="input-title">
<div><h2 id="input-title">Choose a useful portrait</h2><ul><li>Prefer one visible, front-facing face with open eyes and a clear facial outline.</li><li>Use even lighting and enough resolution to preserve detail after cropping.</li><li>Avoid group photos, heavy blur, extreme angles, strong shadows, and facial obstructions.</li><li>Keep the full face inside the preview; preparation reduces input issues but cannot guarantee a particular swap result.</li></ul></div>
<dl class="specs"><div><dt>Input</dt><dd>One JPG, PNG, or WebP image up to 20 MB</dd></div><div><dt>Controls</dt><dd>Output size, horizontal crop focus, and vertical crop focus</dd></div><div><dt>Output</dt><dd>Square 512×512, 768×768, or 1024×1024 RGB PNG</dd></div><div><dt>Boundary</dt><dd>Reference preprocessing only; no face swap model or identity transformation runs here</dd></div></dl>
</section>
<section class="section" aria-labelledby="faq-title">
<h2 id="faq-title">Frequently asked questions</h2>
<details><summary>Does this page perform face swapping?</summary><p>No. It prepares a portrait reference. Face swapping happens later in the LiveFaceSwap AI browser or desktop workflow.</p></details>
<details><summary>Is my portrait uploaded to a server?</summary><p>No. This is a static browser utility. The selected image is decoded, cropped, resized, and exported locally on this page.</p></details>
<details><summary>Which output size should I use?</summary><p>1024×1024 is a practical default when the source contains enough detail. Use 512 or 768 when you need a smaller file.</p></details>
<details><summary>What do the focus sliders change?</summary><p>They move the square crop across a non-square source so you can keep the intended face centered in the exported image.</p></details>
<details><summary>Can I use the output elsewhere?</summary><p>Yes. It is a standard RGB PNG and can be used in other authorized portrait workflows that accept square references.</p></details>
</section>
<footer>
<h2>Continue with LiveFaceSwap AI</h2>
<p>Open the real-time face swap workspace, read the step-by-step guide, or inspect the MIT-licensed preprocessing source.</p>
<nav class="project-links" aria-label="Project links"><a class="button-link primary" href="https://livefaceswap.ai/?utm_source=huggingface&utm_medium=referral&utm_campaign=reference_prep" target="_blank" rel="noopener">LiveFaceSwap AI official website</a><a class="button-link" href="https://livefaceswap.ai/guides/live-face-swap-online?utm_source=huggingface&utm_medium=referral&utm_campaign=reference_prep" target="_blank" rel="noopener">Online face swap guide</a><a class="button-link" href="https://github.com/LiveFaceSwapAI/livefaceswap-reference-prep" target="_blank" rel="noopener">Canonical source</a></nav>
<p>Only process images you own or have permission to use. Do not use portrait references to impersonate, deceive, defraud, harass, or violate another person's rights. Disclose AI-altered media when context requires it.</p>
</footer>
</main>
<script>
const fileInput = document.querySelector("#file");
const exampleButton = document.querySelector("#example");
const sizeInput = document.querySelector("#size");
const focusXInput = document.querySelector("#focus-x");
const focusYInput = document.querySelector("#focus-y");
const focusXValue = document.querySelector("#focus-x-value");
const focusYValue = document.querySelector("#focus-y-value");
const canvas = document.querySelector("#canvas");
const placeholder = document.querySelector(".placeholder");
const downloadButton = document.querySelector("#download");
const context = canvas.getContext("2d", { alpha: false });
let source = null;
async function decodeImage(file) {
if ("createImageBitmap" in window) return createImageBitmap(file, { imageOrientation: "from-image" });
const url = URL.createObjectURL(file);
try { const image = new Image(); image.src = url; await image.decode(); return image; }
finally { URL.revokeObjectURL(url); }
}
function render() {
focusXValue.textContent = Number(focusXInput.value).toFixed(2);
focusYValue.textContent = Number(focusYInput.value).toFixed(2);
if (!source) return;
const outputSize = Number(sizeInput.value);
const width = source.width || source.naturalWidth;
const height = source.height || source.naturalHeight;
const side = Math.min(width, height);
const left = Math.round((width - side) * Number(focusXInput.value));
const top = Math.round((height - side) * Number(focusYInput.value));
canvas.width = outputSize;
canvas.height = outputSize;
context.fillStyle = "#ffffff";
context.fillRect(0, 0, outputSize, outputSize);
context.drawImage(source, left, top, side, side, 0, 0, outputSize, outputSize);
canvas.hidden = false;
placeholder.hidden = true;
downloadButton.disabled = false;
}
fileInput.addEventListener("change", async () => {
const file = fileInput.files && fileInput.files[0];
if (!file) return;
if (file.size > 20 * 1024 * 1024) { alert("Please choose an image smaller than 20 MB."); fileInput.value = ""; return; }
if (source && typeof source.close === "function") source.close();
source = await decodeImage(file);
render();
});
exampleButton.addEventListener("click", async () => {
const syntheticPortrait = `<svg xmlns="http://www.w3.org/2000/svg" width="1200" height="800" viewBox="0 0 1200 800"><defs><linearGradient id="b" x1="0" y1="0" x2="1" y2="1"><stop stop-color="#263d7a"/><stop offset="1" stop-color="#8aa9ef"/></linearGradient></defs><rect width="1200" height="800" fill="url(#b)"/><circle cx="700" cy="310" r="170" fill="#f1c6a8"/><path d="M530 310c10-150 90-210 185-210 115 0 190 86 170 225-54-58-96-94-178-98-68-3-117 24-177 83Z" fill="#29283a"/><ellipse cx="645" cy="315" rx="18" ry="12" fill="#242334"/><ellipse cx="760" cy="315" rx="18" ry="12" fill="#242334"/><path d="M665 390c31 24 69 24 100 0" fill="none" stroke="#a85e68" stroke-width="12" stroke-linecap="round"/><path d="M430 800c28-205 125-310 270-310s242 105 270 310" fill="#18274f"/><circle cx="1010" cy="150" r="70" fill="#f4d77b" opacity=".72"/></svg>`;
if (source && typeof source.close === "function") source.close();
const image = new Image();
image.src = `data:image/svg+xml;charset=utf-8,${encodeURIComponent(syntheticPortrait)}`;
await image.decode();
source = image;
focusXInput.value = "0.58";
focusYInput.value = "0.45";
render();
});
[sizeInput, focusXInput, focusYInput].forEach((input) => input.addEventListener("input", render));
downloadButton.addEventListener("click", () => {
canvas.toBlob((blob) => { if (!blob) return; const link = document.createElement("a"); link.href = URL.createObjectURL(blob); link.download = "livefaceswap-reference.png"; link.click(); setTimeout(() => URL.revokeObjectURL(link.href), 1000); }, "image/png");
});
</script>
</body>
</html>
|