Spaces:
Running
Running
<html lang="en"> | |
<head> | |
<meta charset="UTF-8"> | |
<meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
<title>YOLOSet: Data Factory</title> | |
<script src="https://cdn.tailwindcss.com"></script> | |
<script src="https://unpkg.com/feather-icons"></script> | |
<link rel="stylesheet" href="style.css"> | |
</head> | |
<body class="select-none overflow-hidden"> | |
<canvas id="main-canvas" class="absolute top-0 left-0 w-full h-full outline-none z-0"></canvas> | |
<div id="loader-overlay" | |
class="absolute inset-0 bg-stone-950 flex flex-col items-center justify-center z-50 transition-opacity duration-500"> | |
<div class="loader-spinner"></div> | |
<p id="loader-text" class="mt-4 text-xl tracking-wider text-stone-300">Initializing...</p> | |
</div> | |
<div id="notification-container" | |
class="fixed bottom-5 left-1/2 -translate-x-1/2 z-[60] flex flex-col items-center gap-2 w-[90vw] max-w-md"> | |
</div> | |
<div id="dataset-modal" class="modal-overlay fixed inset-0 z-40 hidden items-center justify-center p-4"> | |
<div class="glass-ui rounded-lg w-full max-w-5xl mx-auto flex flex-col max-h-[90vh]"> | |
<header class="p-5 pb-4 flex-shrink-0 border-b border-white/10"> | |
<h3 class="text-2xl font-bold text-center tracking-wide">Generate Synthetic Dataset</h3> | |
</header> | |
<div class="overflow-y-auto px-6 py-5 flex-grow"> | |
<div class="grid grid-cols-1 lg:grid-cols-5 gap-6"> | |
<div class="lg:col-span-2 flex flex-col gap-6"> | |
<div class="bg-stone-900/50 rounded-lg p-4 border border-white/10"> | |
<h4 class="text-lg font-semibold text-stone-300 mb-4 border-b border-white/10 pb-2">Core | |
Settings</h4> | |
<div class="space-y-4"> | |
<div> | |
<label for="dataset-name" | |
class="block text-sm font-medium text-stone-300 mb-2">Dataset Name</label> | |
<input type="text" id="dataset-name" value="my_synthetic_dataset" | |
class="w-full bg-stone-800/70 p-2 rounded-md border-2 border-stone-600 focus:border-blue-500 outline-none transition"> | |
</div> | |
<div> | |
<label for="dataset-samples" | |
class="block text-sm font-medium text-stone-300 mb-2">Total Number of | |
Samples</label> | |
<input type="number" id="dataset-samples" value="100" min="10" max="100000" | |
class="w-full bg-stone-800/70 p-2 rounded-md border-2 border-stone-600 focus:border-blue-500 outline-none transition"> | |
</div> | |
</div> | |
</div> | |
<div class="bg-stone-900/50 rounded-lg p-4 border border-white/10"> | |
<h4 class="text-lg font-semibold text-stone-300 mb-4 border-b border-white/10 pb-2"> | |
Annotation</h4> | |
<div class="space-y-4"> | |
<div> | |
<label class="block text-sm font-medium text-stone-300 mb-3">Task Type</label> | |
<div class="space-y-2"> | |
<div> | |
<input type="radio" name="dataset-task" value="detection" | |
id="task-detection" class="sr-only"> | |
<label for="task-detection" | |
class="task-radio-label p-3 rounded-lg block cursor-pointer text-left flex items-center gap-3"> | |
<i data-feather="box" class="w-5 h-5 text-blue-400"></i> | |
<div> | |
<h5 class="font-semibold text-sm leading-tight">Object Detection | |
</h5> | |
<p class="text-xs text-stone-400 mt-0.5">Generates bounding boxes | |
(YOLO).</p> | |
</div> | |
</label> | |
</div> | |
<div> | |
<input type="radio" name="dataset-task" value="segmentation" | |
id="task-segmentation" class="sr-only" checked> | |
<label for="task-segmentation" | |
class="task-radio-label p-3 rounded-lg block cursor-pointer text-left flex items-center gap-3"> | |
<i data-feather="pen-tool" class="w-5 h-5 text-blue-400"></i> | |
<div> | |
<h5 class="font-semibold text-sm leading-tight">Instance | |
Segmentation</h5> | |
<p class="text-xs text-stone-400 mt-0.5">Generates polygon masks | |
(YOLO).</p> | |
</div> | |
</label> | |
</div> | |
<div> | |
<input type="radio" name="dataset-task" value="classification" | |
id="task-classification" class="sr-only"> | |
<label for="task-classification" | |
class="task-radio-label p-3 rounded-lg block cursor-pointer text-left flex items-center gap-3"> | |
<i data-feather="tag" class="w-5 h-5 text-blue-400"></i> | |
<div> | |
<h5 class="font-semibold text-sm leading-tight">Image Classification | |
</h5> | |
<p class="text-xs text-stone-400 mt-0.5">Organizes images into class | |
folders.</p> | |
</div> | |
</label> | |
</div> | |
</div> | |
</div> | |
<div id="segmentation-method-group" class="space-y-2 pt-2"> | |
<label class="block text-sm font-medium text-stone-300">Segmentation Method</label> | |
<div class="grid grid-cols-1 sm:grid-cols-3 gap-3"> | |
<div> | |
<input type="radio" name="segmentation-method" value="convex" | |
id="method-convex" class="sr-only"> | |
<label for="method-convex" | |
class="method-radio-label p-3 rounded-lg block cursor-pointer text-center"> | |
<h5 class="font-semibold text-sm">Convex Hull</h5> | |
<p class="text-xs text-stone-400 mt-1">Fastest</p> | |
</label> | |
</div> | |
<div> | |
<input type="radio" name="segmentation-method" value="concave" | |
id="method-concave" class="sr-only"> | |
<label for="method-concave" | |
class="method-radio-label p-3 rounded-lg block cursor-pointer text-center"> | |
<h5 class="font-semibold text-sm">Concave Hull</h5> | |
<p class="text-xs text-stone-400 mt-1">Accurate</p> | |
</label> | |
</div> | |
<div> | |
<input type="radio" name="segmentation-method" value="render" | |
id="method-render" class="sr-only" checked> | |
<label for="method-render" | |
class="method-radio-label p-3 rounded-lg block cursor-pointer text-center"> | |
<h5 class="font-semibold text-sm">Render Mask</h5> | |
<p class="text-xs text-stone-400 mt-1">Slowest</p> | |
</label> | |
</div> | |
</div> | |
<div id="concave-hull-options" style="max-height: 0; opacity: 0; overflow: hidden;"> | |
<div class="pt-3 space-y-2"> | |
<label for="concave-hull-concavity" | |
class="text-sm font-medium text-stone-300 flex justify-between">Concavity | |
<span id="concave-hull-concavity-value">2</span> | |
<span class="tooltip-trigger"> | |
<i data-feather="help-circle" class="w-4 h-4 text-stone-400"></i> | |
<span class="tooltip-content">Lower values create more detailed | |
shapes but can be slower. Higher values are faster and | |
simpler.</span> | |
</span> | |
</label> | |
<input type="range" id="concave-hull-concavity" min="1" max="10" value="2"> | |
</div> | |
</div> | |
<div id="render-mask-options" style="max-height: 0; opacity: 0; overflow: hidden;"> | |
<div class="pt-3 space-y-2"> | |
<label for="mask-simplification-slider" | |
class="text-sm font-medium text-stone-300 flex justify-between">Mask | |
Simplification | |
<span id="mask-simplification-value">0.1</span> | |
<span class="tooltip-trigger"> | |
<i data-feather="help-circle" class="w-4 h-4 text-stone-400"></i> | |
<span class="tooltip-content">Controls mask accuracy. Lower values | |
create more detailed masks (more vertices). Higher values create | |
simpler masks (fewer vertices).</span> | |
</span> | |
</label> | |
<input type="range" id="mask-simplification-slider" min="0.1" max="3" | |
step="0.1" value="0.1"> | |
</div> | |
</div> | |
</div> | |
</div> | |
</div> | |
</div> | |
<div class="lg:col-span-3 flex flex-col gap-6"> | |
<div class="bg-stone-900/50 rounded-lg p-4 border border-white/10"> | |
<h4 class="text-lg font-semibold text-stone-300 mb-4 border-b border-white/10 pb-2">Dataset | |
Splits</h4> | |
<div class="grid grid-cols-2 gap-x-6 gap-y-4"> | |
<div class="flex items-center justify-between"> | |
<label for="include-val-set" | |
class="font-medium text-stone-300 select-none cursor-pointer">Include Validation | |
Set</label> | |
<label class="relative inline-flex items-center cursor-pointer"> | |
<input type="checkbox" id="include-val-set" class="sr-only peer" checked> | |
<div | |
class="w-11 h-6 bg-stone-700 rounded-full peer-checked:bg-blue-600 toggle-bg"> | |
</div> | |
</label> | |
</div> | |
<div class="flex items-center justify-between"> | |
<label for="include-test-set" | |
class="font-medium text-stone-300 select-none cursor-pointer">Include Test | |
Set</label> | |
<label class="relative inline-flex items-center cursor-pointer"> | |
<input type="checkbox" id="include-test-set" class="sr-only peer" checked> | |
<div | |
class="w-11 h-6 bg-stone-700 rounded-full peer-checked:bg-blue-600 toggle-bg"> | |
</div> | |
</label> | |
</div> | |
<div class="col-span-2 space-y-3 pt-2"> | |
<label class="block text-sm font-medium text-stone-300">Split Ratios (<span | |
id="split-ratio-sum">100</span>%)</label> | |
<div class="grid grid-cols-3 gap-2 text-center text-sm"> | |
<div>Train</div> | |
<div>Val</div> | |
<div>Test</div> | |
</div> | |
<div class="relative h-2 flex items-center my-2"> | |
<div id="split-bg-train" class="absolute h-2 rounded-l-lg bg-blue-600"></div> | |
<div id="split-bg-val" class="absolute h-2 bg-green-500"></div> | |
<div id="split-bg-test" class="absolute h-2 rounded-r-lg bg-yellow-500"></div> | |
<input type="range" id="split-train-ratio" min="0" max="100" value="70" | |
class="split-slider w-full cursor-pointer" style="z-index: 10;"> | |
<input type="range" id="split-val-ratio" min="0" max="100" value="90" | |
class="split-slider w-full cursor-pointer" style="z-index: 20;"> | |
</div> | |
<div class="grid grid-cols-3 gap-2 text-center text-xs text-stone-400"> | |
<div id="split-train-display">70%</div> | |
<div id="split-val-display">20%</div> | |
<div id="split-test-display">10%</div> | |
</div> | |
</div> | |
</div> | |
</div> | |
<div class="bg-stone-900/50 rounded-lg p-4 border border-white/10"> | |
<h4 class="text-lg font-semibold text-stone-300 mb-4 border-b border-white/10 pb-2">Sample | |
Augmentation</h4> | |
<div class="space-y-3"> | |
<div class="flex items-center justify-between"> | |
<label for="include-background-toggle" | |
class="font-medium text-stone-300 select-none cursor-pointer flex items-center gap-2">Include | |
Background Samples | |
<span class="tooltip-trigger"> | |
<i data-feather="help-circle" class="w-4 h-4 text-stone-400"></i> | |
<span class="tooltip-content">Generate some images with no objects (negative | |
samples). This helps reduce false positives in detection models.</span> | |
</span> | |
</label> | |
<label class="relative inline-flex items-center cursor-pointer"> | |
<input type="checkbox" id="include-background-toggle" class="sr-only peer" | |
checked> | |
<div | |
class="w-11 h-6 bg-stone-700 rounded-full peer-checked:bg-blue-600 toggle-bg"> | |
</div> | |
</label> | |
</div> | |
<div id="background-ratio-container" class="sliders-container pl-1 space-y-3"> | |
<div> | |
<label for="background-ratio-slider" class="text-sm flex justify-between">Ratio | |
of Backgrounds | |
<span id="background-ratio-value">15%</span> | |
</label> | |
<input type="range" id="background-ratio-slider" min="0" max="50" value="20"> | |
</div> | |
</div> | |
</div> | |
</div> | |
<div class="bg-stone-900/50 rounded-lg p-4 border border-white/10"> | |
<h4 class="text-lg font-semibold text-stone-300 mb-4 border-b border-white/10 pb-2">Scene & | |
Randomization</h4> | |
<div class="grid grid-cols-1 md:grid-cols-2 gap-x-6 gap-y-4"> | |
<div class="space-y-4"> | |
<div> | |
<label class="font-medium text-stone-200 block mb-2">3D Model Source</label> | |
<div class="flex flex-col gap-2"> | |
<label | |
class="flex items-center gap-2 p-2 rounded-md bg-stone-800/50 hover:bg-stone-700/50 cursor-pointer flex-1"><input | |
type="radio" name="model-source" value="current" checked | |
class="h-4 w-4 rounded-full text-blue-500 bg-stone-700 border-stone-500 focus:ring-blue-600"> | |
Use Current Model</label> | |
<label | |
class="flex items-center gap-2 p-2 rounded-md bg-stone-800/50 hover:bg-stone-700/50 cursor-pointer flex-1"><input | |
type="radio" name="model-source" value="random" | |
class="h-4 w-4 rounded-full text-blue-500 bg-stone-700 border-stone-500 focus:ring-blue-600"> | |
Randomize Library</label> | |
</div> | |
<div id="model-source-current-info" | |
class="text-sm text-blue-400 pl-2 hidden mt-2"></div> | |
</div> | |
<div> | |
<label class="font-medium text-stone-200 block mb-2">Background Source</label> | |
<div class="flex flex-col gap-2"> | |
<label | |
class="flex items-center gap-2 p-2 rounded-md bg-stone-800/50 hover:bg-stone-700/50 cursor-pointer flex-1"><input | |
type="radio" name="pano-source" value="current" checked | |
class="h-4 w-4 rounded-full text-blue-500 bg-stone-700 border-stone-500 focus:ring-blue-600"> | |
Use Current Panorama</label> | |
<label | |
class="flex items-center gap-2 p-2 rounded-md bg-stone-800/50 hover:bg-stone-700/50 cursor-pointer flex-1"><input | |
type="radio" name="pano-source" value="random" | |
class="h-4 w-4 rounded-full text-blue-500 bg-stone-700 border-stone-500 focus:ring-blue-600"> | |
Randomize Library</label> | |
</div> | |
<div id="pano-source-current-info" | |
class="text-sm text-blue-400 pl-2 hidden mt-2"></div> | |
</div> | |
</div> | |
<div class="space-y-4"> | |
<div class="control-section space-y-3"> | |
<div class="flex items-center justify-between"> | |
<label for="randomize-model-toggle" | |
class="font-medium text-stone-300 select-none cursor-pointer flex items-center gap-2">Model | |
Transform | |
<span class="tooltip-trigger"><i data-feather="help-circle" | |
class="w-4 h-4 text-stone-400"></i><span | |
class="tooltip-content">Applies random position, rotation, and | |
scale. If 'Use Current Model' is selected, randomization is | |
relative to its current state.</span></span> | |
</label> | |
<label class="relative inline-flex items-center cursor-pointer"> | |
<input type="checkbox" id="randomize-model-toggle" class="sr-only peer" | |
checked> | |
<div | |
class="w-11 h-6 bg-stone-700 rounded-full peer-checked:bg-blue-600 toggle-bg"> | |
</div> | |
</label> | |
</div> | |
<div id="model-sliders" class="sliders-container pl-1 space-y-3" | |
style="max-height: 0; opacity: 0; overflow: hidden;"> | |
<div><label for="position-variance" | |
class="text-sm flex justify-between">Position Var. <span | |
id="position-variance-value">90%</span></label><input | |
type="range" id="position-variance" min="0" max="100" value="90"> | |
</div> | |
<div><label for="rotation-variance" | |
class="text-sm flex justify-between">Rotation Var. <span | |
id="rotation-variance-value">100%</span></label><input | |
type="range" id="rotation-variance" min="0" max="100" value="100"> | |
</div> | |
<div><label for="scale-variance" class="text-sm flex justify-between">Scale | |
Var. <span id="scale-variance-value">10%</span></label><input | |
type="range" id="scale-variance" min="0" max="100" value="10"></div> | |
</div> | |
</div> | |
<div id="camera-randomization-section" | |
class="control-section space-y-3 transition-opacity duration-300"> | |
<div class="flex items-center justify-between"> | |
<label for="randomize-camera-toggle" | |
class="font-medium text-stone-300 select-none cursor-pointer flex items-center gap-2">Camera | |
Viewpoint | |
<span class="tooltip-trigger"><i data-feather="help-circle" | |
class="w-4 h-4 text-stone-400"></i><span | |
class="tooltip-content">Generate images from viewpoints similar | |
to the current camera angle. Only available when using the | |
current panorama.</span></span> | |
</label> | |
<label class="relative inline-flex items-center cursor-pointer"> | |
<input type="checkbox" id="randomize-camera-toggle" class="sr-only peer" | |
checked> | |
<div | |
class="w-11 h-6 bg-stone-700 rounded-full peer-checked:bg-blue-600 toggle-bg"> | |
</div> | |
</label> | |
</div> | |
<div id="camera-sliders" class="sliders-container pl-1 space-y-3" | |
style="max-height: 0; opacity: 0; overflow: hidden;"> | |
<div><label for="horizontal-variance" | |
class="text-sm flex justify-between">Horizontal Var. <span | |
id="horizontal-variance-value">180°</span></label><input | |
type="range" id="horizontal-variance" min="0" max="180" value="180"> | |
</div> | |
<div><label for="vertical-variance" | |
class="text-sm flex justify-between">Vertical Var. <span | |
id="vertical-variance-value">20°</span></label><input | |
type="range" id="vertical-variance" min="0" max="90" value="20"> | |
</div> | |
</div> | |
</div> | |
</div> | |
</div> | |
</div> | |
</div> | |
</div> | |
</div> | |
<div class="flex-shrink-0 flex justify-end gap-3 p-4 border-t border-white/10 bg-stone-900/30"> | |
<button id="cancel-dataset" | |
class="bg-stone-600 px-5 py-2.5 rounded-md font-semibold hover:bg-stone-700 transition-colors disabled:opacity-50">Cancel</button> | |
<button id="start-dataset" | |
class="bg-blue-600 px-5 py-2.5 rounded-md font-semibold hover:bg-blue-700 min-w-[120px] flex justify-center items-center transition-colors disabled:bg-blue-800 disabled:cursor-not-allowed"> | |
<span class="btn-text">Generate</span> | |
<div class="btn-spinner hidden"></div> | |
</button> | |
</div> | |
</div> | |
</div> | |
<div id="progress-container" class="modal-overlay fixed inset-0 z-40 hidden items-center justify-center p-4"> | |
<div class="glass-ui w-full max-w-sm p-6 rounded-lg"> | |
<h3 id="progress-title" class="text-lg font-bold mb-4">Generating Dataset</h3> | |
<div class="flex justify-between items-baseline mb-2"> | |
<div id="progress-label" class="text-sm text-stone-300">Processing...</div> | |
<div id="progress-count" class="text-xs text-stone-400"></div> | |
</div> | |
<div class="w-full bg-stone-700 rounded-full h-2.5"> | |
<div id="progress-bar" class="bg-blue-600 h-2.5 rounded-full transition-all" style="width: 0%"></div> | |
</div> | |
</div> | |
</div> | |
<div id="upload-modal" class="modal-overlay fixed inset-0 z-40 hidden items-center justify-center p-4"> | |
<div class="glass-ui p-6 rounded-lg w-full max-w-md mx-auto flex flex-col gap-4"> | |
<h3 id="upload-title" class="text-2xl font-bold text-center">Upload New Asset</h3> | |
<div id="upload-drop-zone" class="drop-zone p-6 rounded-lg text-center cursor-pointer"> | |
<p class="text-stone-300 pointer-events-none">Drag & Drop File</p> | |
<p class="text-stone-400 text-sm pointer-events-none">or click to browse</p> | |
<input type="file" id="upload-file-input" class="hidden"> | |
</div> | |
<div class="flex items-center text-stone-400"> | |
<hr class="flex-grow border-white/10"><span class="mx-2 text-sm">OR</span> | |
<hr class="flex-grow border-white/10"> | |
</div> | |
<input type="text" id="upload-url-input" | |
class="w-full bg-stone-900/50 p-2 rounded-md border-2 border-stone-600 focus:border-blue-500 outline-none" | |
placeholder="Enter Asset URL"> | |
<div class="flex justify-end gap-3 mt-2"> | |
<button id="upload-cancel-btn" | |
class="bg-stone-600 px-4 py-2 rounded-md font-semibold hover:bg-stone-700 transition-colors">Cancel</button> | |
<button id="upload-load-btn" | |
class="bg-blue-600 px-4 py-2 rounded-md font-semibold hover:bg-blue-700 min-w-[80px] flex justify-center items-center transition-colors disabled:bg-blue-800"> | |
<span class="btn-text">Load</span> | |
<div class="btn-spinner hidden"></div> | |
</button> | |
</div> | |
</div> | |
</div> | |
<div id="controls-bar" | |
class="glass-ui fixed top-4 left-1/2 -translate-x-1/2 z-20 p-2 rounded-lg flex items-center gap-2"> | |
<button id="translate-btn" class="control-btn p-2 rounded hover:bg-white/10" data-mode="translate" | |
title="Translate (W)"><i data-feather="move"></i></button> | |
<button id="rotate-btn" class="control-btn p-2 rounded hover:bg-white/10" data-mode="rotate" | |
title="Rotate (E)"><i data-feather="rotate-cw"></i></button> | |
<button id="scale-btn" class="control-btn p-2 rounded hover:bg-white/10" data-mode="scale" title="Scale (R)"><i | |
data-feather="maximize"></i></button> | |
<div class="w-px h-6 bg-white/10 mx-1"></div> | |
<div class="flex items-center gap-2 px-2" title="Toggle Day/Night"> | |
<i data-feather="sun" class="w-5 h-5 text-stone-400"></i> | |
<label class="relative inline-flex items-center cursor-pointer"> | |
<input type="checkbox" id="day-night-toggle" class="sr-only peer"> | |
<div class="w-11 h-6 bg-stone-700 rounded-full peer-checked:bg-blue-600 toggle-bg"></div> | |
</label> | |
<i data-feather="moon" class="w-5 h-5 text-stone-400"></i> | |
</div> | |
<div class="w-px h-6 bg-white/10 mx-1"></div> | |
<button id="generate-dataset-btn" class="p-2 rounded hover:bg-blue-600/50" title="Generate Dataset (G)"><i | |
data-feather="database"></i></button> | |
</div> | |
<button id="model-panel-trigger" | |
class="panel-trigger glass-ui absolute top-1/2 -translate-y-1/2 left-0 z-20 rounded-r-lg p-3"><i | |
data-feather="box"></i></button> | |
<button id="panorama-panel-trigger" | |
class="panel-trigger glass-ui absolute top-1/2 -translate-y-1/2 right-0 z-20 rounded-l-lg p-3"><i | |
data-feather="image"></i></button> | |
<aside id="model-panel" | |
class="side-panel left glass-ui fixed top-0 left-0 h-full w-[80vw] sm:w-80 z-30 flex flex-col"> | |
<header class="flex-shrink-0 flex justify-between items-center p-4 border-b border-white/10"> | |
<h2 class="text-xl font-bold">Models</h2> | |
<div> | |
<button id="add-model-btn" class="p-2 rounded-full hover:bg-blue-600/50" title="Add new model"><i | |
data-feather="plus"></i></button> | |
<button id="close-model-panel" class="p-2 rounded-full hover:bg-stone-700"><i | |
data-feather="x"></i></button> | |
</div> | |
</header> | |
<div id="model-selector" | |
class="panel-content flex-grow overflow-y-auto p-4 grid grid-cols-1 md:grid-cols-2 gap-4"></div> | |
</aside> | |
<aside id="panorama-panel" | |
class="side-panel right glass-ui fixed top-0 right-0 h-full w-[80vw] sm:w-80 z-30 flex flex-col"> | |
<header class="flex-shrink-0 flex justify-between items-center p-4 border-b border-white/10"> | |
<h2 class="text-xl font-bold">Panoramas</h2> | |
<div> | |
<button id="add-panorama-btn" class="p-2 rounded-full hover:bg-blue-600/50" title="Add new panorama"><i | |
data-feather="plus"></i></button> | |
<button id="close-panorama-panel" class="p-2 rounded-full hover:bg-stone-700"><i | |
data-feather="x"></i></button> | |
</div> | |
</header> | |
<div id="panorama-gallery" | |
class="panel-content flex-grow overflow-y-auto p-4 grid grid-cols-1 md:grid-cols-2 gap-4"></div> | |
</aside> | |
<script type="importmap"> | |
{ | |
"imports": { | |
"three": "https://unpkg.com/three@0.160.0/build/three.module.js", | |
"three/addons/": "https://unpkg.com/three@0.160.0/examples/jsm/", | |
"concaveman": "https://cdn.skypack.dev/concaveman", | |
"gsap": "https://cdn.jsdelivr.net/npm/gsap@3.12.5/+esm", | |
"jszip": "https://cdn.jsdelivr.net/npm/jszip@3.10.1/+esm" | |
} | |
} | |
</script> | |
<script type="module" src="core.js"></script> | |
</body> | |
</html> |