Download index.html from AK51/MoonFlash: direct link, hf CLI and curl.
- Browser
- Download file 10.3 kB
-
https://huggingface.co/spaces/AK51/MoonFlash/resolve/main/index.html
- Command line
-
hf download hf://spaces/AK51/MoonFlash/index.html
-
curl -L -o index.html https://huggingface.co/spaces/AK51/MoonFlash/resolve/main/index.html
10.3 kB
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8" /> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0" /> | |
| <title>Lunar Impact Flash Detector</title> | |
| <link rel="stylesheet" href="styles.css" /> | |
| </head> | |
| <body> | |
| <header class="app-header"> | |
| <div class="brand"> | |
| <div class="moon-icon" aria-hidden="true"></div> | |
| <div> | |
| <h1>Lunar Impact Flash Detector</h1> | |
| <p class="subtitle">Upload lunar earthshine video · detect transient impact flashes</p> | |
| </div> | |
| </div> | |
| <a class="ref-link" href="https://science.nasa.gov/citizen-science/impact-flash/" target="_blank" rel="noopener">About impact flashes ↗</a> | |
| </header> | |
| <main class="layout"> | |
| <!-- LEFT: viewer --> | |
| <section class="viewer-panel"> | |
| <div id="dropZone" class="drop-zone"> | |
| <input type="file" id="fileInput" accept="video/mp4,video/*" hidden /> | |
| <div class="drop-inner"> | |
| <div class="upload-glyph" aria-hidden="true">⇪</div> | |
| <p class="drop-title">Drop an MP4 here or click to browse</p> | |
| <p class="drop-hint">A sample video is preloaded so you can try it right away. Best results with steady, monochrome-ish earthshine footage of the dark limb of the Moon.</p> | |
| </div> | |
| </div> | |
| <div id="stage" class="stage hidden"> | |
| <div id="viewport" class="viewport"> | |
| <div class="video-wrap" id="videoWrap"> | |
| <video id="video" playsinline muted></video> | |
| <canvas id="overlay" class="overlay"></canvas> | |
| </div> | |
| <div class="zoom-controls"> | |
| <button id="zoomOutBtn" class="btn btn-sm" title="Zoom out">−</button> | |
| <span id="zoomLabel" class="zoom-label">100%</span> | |
| <button id="zoomInBtn" class="btn btn-sm" title="Zoom in">+</button> | |
| <button id="zoomResetBtn" class="btn btn-ghost btn-sm" title="Reset zoom">Reset</button> | |
| </div> | |
| </div> | |
| <div class="transport"> | |
| <button id="prevFrameBtn" class="btn" title="Previous frame">⏮</button> | |
| <button id="playBtn" class="btn" title="Play / Pause">►</button> | |
| <button id="nextFrameBtn" class="btn" title="Next frame">⏭</button> | |
| <input id="seek" class="seek" type="range" min="0" max="1000" value="0" step="1" /> | |
| <span id="timeLabel" class="time-label">0:00 / 0:00</span> | |
| <button id="newFileBtn" class="btn btn-ghost" title="Load a different file">Change file</button> | |
| </div> | |
| <div class="frame-readout"> | |
| <span id="frameLabel" class="frame-label">frame –</span> | |
| <span class="frame-hint">Use ⏮ / ⏭ or the ← → arrow keys to step frames. Scroll or use +/− to zoom; drag to pan when zoomed.</span> | |
| </div> | |
| <div class="analysis-bar"> | |
| <button id="analyzeBtn" class="btn btn-primary">Analyze for flashes</button> | |
| <div id="progressWrap" class="progress-wrap hidden"> | |
| <div class="progress-track"><div id="progressBar" class="progress-bar"></div></div> | |
| <span id="progressText" class="progress-text">0%</span> | |
| <button id="cancelBtn" class="btn btn-ghost btn-sm">Cancel</button> | |
| </div> | |
| </div> | |
| </div> | |
| </section> | |
| <!-- RIGHT: controls + results --> | |
| <aside class="side-panel"> | |
| <div class="card"> | |
| <button type="button" id="settingsToggle" class="collapse-header" aria-expanded="false" aria-controls="settingsBody"> | |
| <h2>Detection settings</h2> | |
| <span class="chevron" aria-hidden="true">▾</span> | |
| </button> | |
| <div id="settingsBody" class="collapse-body collapsed"> | |
| <label class="field"> | |
| <span class="field-label">Sensitivity | |
| <span class="field-value" id="sensVal">Medium</span> | |
| </span> | |
| <input id="sensitivity" type="range" min="1" max="5" value="3" step="1" /> | |
| <span class="field-help">Higher = detects fainter flashes (more false positives).</span> | |
| </label> | |
| <label class="field"> | |
| <span class="field-label">Min flash size | |
| <span class="field-value" id="minSizeVal">2 px</span> | |
| </span> | |
| <input id="minSize" type="range" min="1" max="30" value="2" step="1" /> | |
| <span class="field-help">Ignore bright blobs smaller than this (removes hot pixels).</span> | |
| </label> | |
| <label class="field"> | |
| <span class="field-label">Max flash size | |
| <span class="field-value" id="maxSizeVal">1500 px</span> | |
| </span> | |
| <input id="maxSize" type="range" min="50" max="8000" value="1500" step="50" /> | |
| <span class="field-help">Ignore very large brightenings (clouds, focus, whole-limb glare).</span> | |
| </label> | |
| <label class="field"> | |
| <span class="field-label">Min flash duration | |
| <span class="field-value" id="minDurVal">0 ms</span> | |
| </span> | |
| <input id="minDur" type="range" min="0" max="500" value="0" step="10" /> | |
| <span class="field-help">Ignore flashes shorter than this.</span> | |
| </label> | |
| <label class="field"> | |
| <span class="field-label">Max flash duration | |
| <span class="field-value" id="maxDurVal">800 ms</span> | |
| </span> | |
| <input id="maxDur" type="range" min="100" max="3000" value="800" step="50" /> | |
| <span class="field-help">Reject persistent lights (satellites, glare) that last longer.</span> | |
| </label> | |
| <label class="field checkbox-field"> | |
| <input id="showMarkers" type="checkbox" checked /> | |
| <span>Show flash markers</span> | |
| </label> | |
| <label class="field checkbox-field"> | |
| <input id="highlightPlayback" type="checkbox" checked /> | |
| <span>Highlight flashes during playback</span> | |
| </label> | |
| </div> | |
| </div> | |
| <div class="card results-card"> | |
| <div class="results-head"> | |
| <h2>Detected flashes <span id="flashCount" class="pill">0</span></h2> | |
| <button id="exportBtn" class="btn btn-ghost btn-sm" disabled>Export CSV</button> | |
| </div> | |
| <div id="emptyState" class="empty-state">No analysis yet. Load a video and click <strong>Analyze</strong>.</div> | |
| <div id="resultsScroll" class="results-scroll"> | |
| <table id="resultsTable" class="results-table hidden"> | |
| <thead> | |
| <tr> | |
| <th>#</th> | |
| <th>Time / Frame</th> | |
| <th>X, Y</th> | |
| <th>Duration</th> | |
| <th>Size</th> | |
| <th>Peak</th> | |
| <th>Clip</th> | |
| </tr> | |
| </thead> | |
| <tbody id="resultsBody"></tbody> | |
| </table> | |
| </div> | |
| </div> | |
| <div class="card note-card"> | |
| <button type="button" id="aboutToggle" class="collapse-header" aria-expanded="false" aria-controls="aboutBody"> | |
| <h2>About & how it works</h2> | |
| <span class="chevron" aria-hidden="true">▾</span> | |
| </button> | |
| <div id="aboutBody" class="collapse-body collapsed"> | |
| <p>This tool scans a video of the Moon for <strong>impact flashes</strong> — the brief glints of light produced when a meteoroid strikes the lunar surface. Everything runs in your browser; the video is never uploaded.</p> | |
| <h3 class="about-step">1. Remove the steady background</h3> | |
| <p>The Moon drifts and jitters slightly between frames (tracking error, atmosphere, a little rotation) and the earthshine glow changes slowly. The software keeps a <strong>rolling average of recent frames</strong> as a model of the quiet, slowly-changing scene, then subtracts it from the current frame. What survives is only what suddenly <em>changed</em> — the steady Moon largely cancels out.</p> | |
| <h3 class="about-step">2. Keep only compact bright spots</h3> | |
| <p>Camera shake smears brightness across wide areas, which looks like change everywhere. A <strong>top-hat filter</strong> subtracts a blurred copy of the difference image so broad, soft glows disappear and only <strong>small, sharply-peaked bright points</strong> remain — the shape an impact flash makes.</p> | |
| <h3 class="about-step">3. Group pixels into blobs</h3> | |
| <p>Neighbouring bright pixels are joined into blobs. Each blob's position, size, shape and peak brightness are measured.</p> | |
| <h3 class="about-step">4. Reject look-alikes</h3> | |
| <p>A blob is dismissed unless it behaves like a real flash:</p> | |
| <ul class="about-list"> | |
| <li><strong>Point-like, not a streak</strong> — thin, elongated shimmer along the Moon's bright edge is rejected.</li> | |
| <li><strong>Right size</strong> — not a single hot pixel, not a huge brightening (cloud, focus change, glare).</li> | |
| <li><strong>Brief and transient</strong> — it must rise sharply and then fade within the duration limits; steady lights (stars, satellites) are rejected.</li> | |
| <li><strong>Strong enough</strong> — its peak must clear a confidence threshold set by the Sensitivity control.</li> | |
| </ul> | |
| <h3 class="about-step">5. Track and report</h3> | |
| <p>Surviving blobs are linked across consecutive frames into a single event, then reported with the <strong>location, the frame(s) it appears in, duration, size and peak brightness</strong>. The method follows established lunar-flash work such as <a href="https://science.nasa.gov/citizen-science/impact-flash/" target="_blank" rel="noopener">NASA's impact-flash citizen science</a> and the <a href="https://users.aber.ac.uk/atc/alfi.htm" target="_blank" rel="noopener">ALFI project</a>.</p> | |
| <p class="disclaimer">This is a screening tool, not confirmation. A real lunar impact must be verified against a second simultaneous recording to rule out satellites, cosmic-ray hits, and camera noise.</p> | |
| </div> | |
| </div> | |
| <footer class="credit">Created by Andy Kong</footer> | |
| </aside> | |
| </main> | |
| <div id="toast" class="toast hidden"></div> | |
| <script src="detector.js"></script> | |
| <script src="app.js"></script> | |
| </body> | |
| </html> | |