{"id":20439,"date":"2026-10-25T15:24:35","date_gmt":"2026-10-25T07:24:35","guid":{"rendered":"https:\/\/bruneiastronomy.org\/web\/?page_id=20439"},"modified":"2026-10-26T21:57:07","modified_gmt":"2026-10-26T13:57:07","slug":"skycam","status":"publish","type":"page","link":"https:\/\/bruneiastronomy.org\/web\/skycam\/","title":{"rendered":"SkyCam &amp; Meteor Station"},"content":{"rendered":"\n<h2 class=\"wp-block-heading\"><strong>Meteor Station<\/strong><\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Maintained by the Astronomical Society of Brunei Darussalam (PABD), this meteor station portal offers a real-time view of local weather and celestial conditions alongside automatically captured snapshots of meteors, satellites, and transient sky events. Detected meteors are highlighted with automated overlays featuring precise red bounding boxes and calibrated constellation art.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Build Your Own Station:<\/strong> Interested in astronomy and meteor tracking in Brunei Darussalam? We invite anyone keen to build their own station to <a href=\"https:\/\/bruneiastronomy.org\/web\/contact-us\/\" data-type=\"page\" data-id=\"1464\">reach out to PABD<\/a> and join our observation network!<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<meta charset=\"UTF-8\">\n<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n<title>Time and Date<\/title>\n<style>\n    .clock-container {\n        font-family: Arial, sans-serif;\n        display: block;        \/* Changed from inline-block *\/\n        margin: 0 auto;        \/* Centers the block on the page *\/\n        width: fit-content;    \/* Keeps the container sized around the text *\/\n        text-align: center;    \/* Centers time and date text inside *\/\n    }\n\n    #time {\n        font-size: 3rem;\n        font-weight: bold;\n        line-height: 1.1;\n    }\n\n    #date {\n        font-size: 1.2rem;\n        margin-top: 5px;\n        opacity: 0.8;\n    }\n<\/style>\n\n<div class=\"clock-container\">\n    <div id=\"time\">00:00:00<\/div>\n    <div id=\"date\">Loading date&#8230;<\/div>\n<\/div>\n\n<script>\n    function updateClock() {\n        const now = new Date();\n\n        \/\/ Format Time (HH:MM:SS)\n        const hours = String(now.getHours()).padStart(2, '0');\n        const minutes = String(now.getMinutes()).padStart(2, '0');\n        const seconds = String(now.getSeconds()).padStart(2, '0');\n        document.getElementById('time').textContent = `${hours}:${minutes}:${seconds}`;\n\n        \/\/ Format Date (e.g., Monday, October 26, 2026)\n        const options = { weekday: 'long', year: 'numeric', month: 'long', day: 'numeric' };\n        document.getElementById('date').textContent = now.toLocaleDateString(undefined, options);\n    }\n\n    \/\/ Run immediately and update every second\n    updateClock();\n    setInterval(updateClock, 1000);\n<\/script>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<script language=\u201dJavaScript\u201d type=\u201dtext\/javascript\u201d>\n  interval = 30000;\n  imageUrl = \u201chttps:\/\/www.bruneiastronomy.org\/allskycam\/AllSkEye_Latest.jpg\u201c;\n\n  function Refresh() {\n    var im = document.getElementById(\u2018image0\u2019);\n\n  LoadAndSetNewImageIfFound(im, imageUrl);\n\n    setTimeout(\u201cRefresh()\u201d, interval);\n  }\n\nfunction LoadAndSetNewImageIfFound(image, url){\nvar newImg = new Image;\nnewImg.onload = function() {\nimage.src = this.src + \u201c?\u201d + new Date().getTime();\n}\nnewImg.src = url;\n}\n Refresh();\n<\/script>\n\n\n\n<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n  <meta charset=\"UTF-8\">\n  <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n  <title>Live Camera Indicator<\/title>\n  <style>\n    \/* Reset & Base Layout for the Header Row *\/\n    * {\n      box-sizing: border-box;\n      margin: 0;\n      padding: 0;\n    }\n\n    \/* Container holding both the badge and the heading on the same line *\/\n    .camera-header {\n      display: flex;\n      align-items: center;\n      gap: 12px; \/* Space between the badge and the title *\/\n    }\n\n    \/* Live Badge Container *\/\n    .live-badge {\n      display: inline-flex;\n      align-items: center;\n      gap: 8px;\n      background-color: rgba(0, 0, 0, 0.75);\n      border: 1px solid rgba(239, 68, 68, 0.4);\n      padding: 6px 14px;\n      border-radius: 9999px; \/* Pill shape *\/\n      box-shadow: 0 4px 12px rgba(0, 0, 0, 0.2);\n    }\n\n    \/* Red Dot Container *\/\n    .dot-container {\n      position: relative;\n      width: 10px;\n      height: 10px;\n      display: flex;\n      align-items: center;\n      justify-content: center;\n    }\n\n    \/* Solid Core Red Dot *\/\n    .red-dot {\n      width: 10px;\n      height: 10px;\n      background-color: #ef4444; \/* Bright Red *\/\n      border-radius: 50%;\n      position: absolute;\n    }\n\n    \/* Pulsing Outer Ring for the Dot *\/\n    .red-dot-pulse {\n      width: 10px;\n      height: 10px;\n      background-color: #ef4444;\n      border-radius: 50%;\n      position: absolute;\n      animation: dotPulse 1.8s ease-out infinite;\n    }\n\n    \/* Flashing \"LIVE\" Text *\/\n    .live-text {\n      color: #ef4444;\n      font-size: 14px;\n      font-weight: 800;\n      letter-spacing: 1.5px;\n      text-transform: uppercase;\n      font-family: system-ui, -apple-system, sans-serif;\n      animation: textFlash 1.5s ease-in-out infinite alternate;\n    }\n\n    \/* Camera Heading Style *\/\n    .camera-title {\n      font-family: system-ui, -apple-system, sans-serif;\n      font-size: 1.5rem;\n      font-weight: 700;\n      color: inherit; \/* Inherits text color from your page *\/\n    }\n\n    \/* --- Keyframe Animations --- *\/\n\n    @keyframes dotPulse {\n      0% {\n        transform: scale(1);\n        opacity: 0.9;\n      }\n      100% {\n        transform: scale(2.8);\n        opacity: 0;\n      }\n    }\n\n    @keyframes textFlash {\n      0% {\n        opacity: 1;\n        text-shadow: 0 0 8px rgba(239, 68, 68, 0.8);\n      }\n      50% {\n        opacity: 0.3;\n        text-shadow: none;\n      }\n      100% {\n        opacity: 1;\n        text-shadow: 0 0 8px rgba(239, 68, 68, 0.8);\n      }\n    }\n  <\/style>\n<\/head>\n<body>\n\n  <!-- Header Row holding badge and h2 side-by-side -->\n  <div class=\"camera-header\">\n    <div class=\"live-badge\" role=\"status\" aria-label=\"Currently Live\">\n      <div class=\"dot-container\">\n        <div class=\"red-dot-pulse\"><\/div>\n        <div class=\"red-dot\"><\/div>\n      <\/div>\n      <span class=\"live-text\">LIVE<\/span>\n    <\/div>\n    \n    <h2 class=\"camera-title\">SkyCam 1 (Tutong, Brunei Darussalam)<\/h2>\n  <\/div>\n\n<\/body>\n<\/html>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Location: Mini Observatory, Keriam. <\/strong>|  Direction: Azimuth 120 (East); Elevation 80 degree |  Equipment: Tapo C325WB, Python Meteor Detection | Frequency: Refresh rate @ 60 secs<\/p>\n\n\n\n<style>\n  .progress-bar-container {\n    width: 100%;\n    max-width: 800px;\n    background-color: #ddd;\n    height: 8px;\n    border-radius: 4px;\n    overflow: hidden;\n    margin: 15px 0;\n  }\n  \n  .progress-bar {\n    height: 100%;\n    width: 0%;\n    background-color: #4CAF50; \/* Green bar *\/\n    transition: width 0.1s linear, background-color 0.3s ease;\n  }\n<\/style>\n\n<!-- ================= 1. LIVE FEED SECTION ================= -->\n<h3>Live Feed:<\/h3>\n<div>\n  <img decoding=\"async\" id=\"liveFeedImg\" src=\"\" alt=\"All Sky Camera\" style=\"max-width: 100%;\">\n<\/div>\n<div class=\"progress-bar-container\">\n  <div id=\"liveFeedBar\" class=\"progress-bar\"><\/div>\n<\/div>\n\n\n<!-- ================= 2. LATEST DETECTION SECTION ================= -->\n<br>\n<h3>Latest Detection:<\/h3>\n<div>\n  <img decoding=\"async\" id=\"detectionImg\" src=\"\" alt=\"Object Detection\" style=\"max-width: 100%;\">\n<\/div>\n<div class=\"progress-bar-container\">\n  <div id=\"detectionBar\" class=\"progress-bar\"><\/div>\n<\/div>\n\n\n<!-- ================= SCRIPT LOGIC ================= -->\n<script type=\"text\/javascript\">\n  function initImageWidget(imageId, progressBarId, imageUrl, refreshIntervalMs) {\n    var updateIntervalMs = 100;\n    var timeElapsed = 0;\n    var isDownloading = false;\n\n    var imageElement = document.getElementById(imageId);\n    var progressBar = document.getElementById(progressBarId);\n\n    function resetTimer() {\n      timeElapsed = 0;\n      isDownloading = false;\n      if (progressBar) {\n        progressBar.style.width = '0%';\n        progressBar.style.backgroundColor = \"#4CAF50\"; \/\/ Turn back to green\n      }\n    }\n\n    function refreshImage() {\n      if (!imageElement) return;\n      var newImg = new Image();\n      \n      \/\/ Runs ONLY when the image finishes downloading successfully\n      newImg.onload = function() {\n        imageElement.src = newImg.src; \/\/ Apply fetched & timestamped source\n        resetTimer();\n      };\n      \n      \/\/ If the server fails or image is missing, reset the bar anyway\n      newImg.onerror = function() {\n        resetTimer();\n      };\n\n      \/\/ Force browser to bypass cache using a unique timestamp\n      newImg.src = imageUrl + \"?t=\" + new Date().getTime();\n    }\n\n    \/\/ Start progress bar ticker\n    setInterval(function() {\n      if (isDownloading) return; \/\/ Pause if currently downloading\n\n      timeElapsed += updateIntervalMs;\n      var percentage = (timeElapsed \/ refreshIntervalMs) * 100;\n      \n      if (progressBar) {\n        progressBar.style.width = percentage + '%';\n      }\n\n      \/\/ When time is up, trigger download and turn progress bar orange\n      if (timeElapsed >= refreshIntervalMs) {\n        isDownloading = true;\n        if (progressBar) progressBar.style.backgroundColor = \"#ff9800\"; \n        refreshImage();\n      }\n    }, updateIntervalMs);\n\n    \/\/ Initial load when page opens\n    refreshImage();\n  }\n\n  \/\/ Initialize both widgets independently when the window loads\n  window.onload = function() {\n    \/\/ Live Feed (Refreshes every 60 seconds)\n    initImageWidget(\"liveFeedImg\", \"liveFeedBar\", \"https:\/\/bruneiastronomy.org\/allskycam\/AllSkEye_Latest.jpg\", 60000);\n    \n    \/\/ Latest Detection (Refreshes every 60 seconds)\n    initImageWidget(\"detectionImg\", \"detectionBar\", \"https:\/\/bruneiastronomy.org\/allskycam\/latest_detection.jpg\", 60000);\n  };\n<\/script>\n\n\n\n<p class=\"wp-block-paragraph\"><em>Note: When meteor (also including satellite, airplane, helicopter, red sprite etc)  results are detected, the latest trajectory image is automatically uploaded and shown above. This image features two overlays: a red rectangle highlighting the detected meteor\/object, and constellation art positioned accurately according to the night sky calibration.<\/em><\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">Next Project: All<strong>SkyCamera<\/strong> 2<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">In progress&#8230;<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Meteor Station Maintained by the Astronomical Society of Brunei Darussalam (PABD), this meteor station portal offers a real-time view of local weather and celestial conditions alongside automatically captured snapshots of [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"parent":0,"menu_order":0,"comment_status":"open","ping_status":"closed","template":"","meta":{"_themeisle_gutenberg_block_has_review":false,"footnotes":""},"class_list":["post-20439","page","type-page","status-publish","hentry"],"_links":{"self":[{"href":"https:\/\/bruneiastronomy.org\/web\/wp-json\/wp\/v2\/pages\/20439","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/bruneiastronomy.org\/web\/wp-json\/wp\/v2\/pages"}],"about":[{"href":"https:\/\/bruneiastronomy.org\/web\/wp-json\/wp\/v2\/types\/page"}],"author":[{"embeddable":true,"href":"https:\/\/bruneiastronomy.org\/web\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/bruneiastronomy.org\/web\/wp-json\/wp\/v2\/comments?post=20439"}],"version-history":[{"count":43,"href":"https:\/\/bruneiastronomy.org\/web\/wp-json\/wp\/v2\/pages\/20439\/revisions"}],"predecessor-version":[{"id":20547,"href":"https:\/\/bruneiastronomy.org\/web\/wp-json\/wp\/v2\/pages\/20439\/revisions\/20547"}],"wp:attachment":[{"href":"https:\/\/bruneiastronomy.org\/web\/wp-json\/wp\/v2\/media?parent=20439"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}