~firefoxmp3
45 itemsDownload ./*

..
1.mp3
10.mp3
11.mp3
12.mp3
13.mp3
14.mp3
15.mp3
16.mp3
17.mp3
18.mp3
19.mp3
2.mp3
20.mp3
21.mp3
22.mp3
23.mp3
24.mp3
25.mp3
26.mp3
27.mp3
28.mp3
29.mp3
3.mp3
30.mp3
31.mp3
32.mp3
33.mp3
34.mp3
4.mp3
5.mp3
6.mp3
7.mp3
8.mp3
9.mp3
candleflame.png
christmastreeship_sunlit_wide.png
christmastreeship_wide.png
index.html
ship-lcd.html
spaceship.png
spaceship_sunlit.png
spaceship_sunlit_wide.png
spaceship_wide.png
stars-transparent.html
stars3d-transparent.html


mp3index.html
78 KB• 210•  1 week ago•  DownloadRawClose
1 week ago•  210

{}
<!DOCTYPE html>
<!--

    Author:        Twily  2015 - 2025
    Website:       http://twily.info/

    AI Generated music album, (no lora), using MusicGen from
    hugginface run locally on home computer for experimenting.

    Delivered in a very nostalgic inspired Winamp, (re)designed
    by myself. Help from grok to setup visualizer and equalizer.

-->
<html>
<head>
<title>Twily AI Music</title>
<meta http-equiv="Content-Type" content="text/html;charset=UTF-8" />
<meta name='viewport' content='width=device-width,initial-scale=1'>

<!--<link id="favicon" rel="shortcut icon" href="http://twily.info/favicon.ico" />-->
<!--<link rel="stylesheet" type="text/css" href="./css/style.css?v=1" />-->
<style type="text/css">
html,body {
    width: 100%; height: 100%; margin: 0; padding: 0; overflow: auto;
    font-family: "Droid Sans", "Liberation Sans", "DejaVu Sans", "Segoe UI", Sans;
    font-size: 12pt; font-weight: bold;
    background: #02161c; color: #AAABAD;
    user-select: none;
    /*overflow-x: hidden;
    overflow-Y: auto;*/
    overflow: hidden;
}
* {
  box-sizing: border-box;

  /* Firefox and modern browsers */
  scrollbar-color: #525793 #1A224B;
  scrollbar-width: 8px;

  /* WebKit-based browsers */
  &::-webkit-scrollbar {
    width: 8px;
    background-color: #1A224B; /* no change */
  }

  &::-webkit-scrollbar-track {
    background-color: #1A224B; /* back */
    border-radius: 1px;
  }

  &::-webkit-scrollbar-thumb {
    background-color: #525793; /* drag */
    border-radius: 1px;
    &:hover {
      background-color: #7D80AA; /* no effect */
    }
  }
}
*:focus { outline: none !important; }

a:link, a:visited { color: #606163; text-decoration: none; }
a:hover, a:active { color: #0f0; }

a.red:hover, a.red:active { color: #f06 !important; }
a.blue:hover, a.blue:active { color: #06f !important; }

#log {
    position: fixed; top: 0; left: 0;
    background: #111;
    color: #f02;
    border: 2px solid #600;
    padding: 8px;
    z-index: 1000000;
    pointer-events: none;
    display: none /*inline-block*/;
}

.tbl { display: table; width: 100%; }
.tr { display: table-row; }
.td { display: table-cell; vertical-align: middle; /*border: 1px solid transparent; box-shadow: inset 0 0 2px 2px #000;*/ }

.sidebar { width: 28px; min-width: 28px; }

/*
#wrap { width: 100%; height: 100%; display: flex; position: absolute; z-index: 9; }
#wrap #box { padding: 128px; background: #111113; margin: auto; display: inline-block; position: relative; }*/

.brd { border-top: 1px solid #620; }

.frm {
    width: 100%; height: 100%;
    position: relative;
    margin: 0; padding: 0;
}
#backdrop {
    position: fixed; top: 0; left: 0; z-index: 1;
    width: 100%; height: 100%;
}
#above {
    position: absolute; top: 0; left: 0; z-index: 10000;
    width: 100%; height: 100%;
}
#backlay {
    position: fixed; top: 0; left: 0; z-index: 2;
    width: 100%; height: 100%;
    background: transparent;
    background: radial-gradient(ellipse at 50%, rgba(26,72,68,.25), rgba(44,53,70,.25) 50%, transparent 100%);
    /*background: radial-gradient(ellipse at 50%, transparent, rgba(35,52,84,.25) 50%, rgba(52,112,114,.25) 100%);*/
    opacity: 1;
}
#lcd_cont {
    position: fixed; top: 0; left: 0;
    z-index: 5000;
    width: 100%; height: 100%;
    background: transparent;
    pointer-events: none;
    opacity: 0; /* start hidden with ship */
    visibility: hidden;
}
#lcd_frm {
    width: 0;
    height: 0;
    position: absolute;
    /*top: 443px; left: 50px;*/
    top: 200px; left: 50%; margin-left: -214px;
    /*top: 50%; left: 50%;
    margin-left: -212px;
    margin-top: -344px;*/
    transform: perspective(300px) rotateX(-3deg) scale(1);
}

.td label:nth-child(1) {
    margin-right: 10px;
}

.playing.selected::before {
    content: "►";
    /*content: "♬"▶;*/
}
.track.selected {
    color: #0f8;
}

#triangle.on::before, #triangleMini.on::before {
    font-size: 22pt; color: #0f0;
    content: "►";
    position: absolute; z-index: 100;
}

.pad18 {
    padding: 4px;
}

#player_frm {
    width: 100%;
    /*min-height: 443px;*/
}

#player {
    border: 1px solid #620;
    border-radius: 2px;
    width: 100%;
    max-width: 602px;
    text-align: left;
    box-shadow: 0 0 600px #00f9cb36;
    position: relative;
    z-index: 10;
}

.btn {
    border-right: 1px solid #2f303c;
    border-bottom: 1px solid #2f303c;
    border-left: 1px solid #747cd3;
    border-top: 1px solid #747cd3;
    background: #525793;
    color: #0f0;
    -webkit-appearance: none; border-radius: 2px; cursor: pointer;
}
.btn:hover {
    background: #5f5ea9;
}
.btn:active, .btn.tog {
    border-top: 1px solid #2f303c;
    border-left: 1px solid #2f303c;
    border-right: 1px solid #747cd3;
    border-bottom: 1px solid #747cd3;
    background: #3e4270;
}

#bar {
    /*position: fixed; bottom: 0; left: 0;*/
    display: block; height: 7px; width: 100%;
    background: #525793;
    background: linear-gradient(0deg, #2d3a7e, #4d2f95);
    border-top: 1px solid #2f303c;
    border-left: 1px solid #2f303c;
    border-right: 1px solid #747cd3;
    border-bottom: 1px solid #747cd3;
}
#progress {
    display: block; height: 100%; width: 0%;
    background: #0f0;
    background: linear-gradient(0deg, #01af01, #00ff2c);
}
#lbloop { display: none; }
#lbshuf { display: none; }

.center { text-align: center; }
.grey {
    padding-left: 6px;
    color: #666;
    font-size: 8pt;
    font-family: monospace;
}

#vol {
    /*position: fixed; bottom: 0; left: 0;*/
    display: inline-block; height: 7px; width: 100px;
    background: #525793;
    background: linear-gradient(0deg, #2d3a7e, #4d2f95);
    border-top: 1px solid #2f303c;
    border-left: 1px solid #2f303c;
    border-right: 1px solid #747cd3;
    border-bottom: 1px solid #747cd3;
}
#level {
    display: block; height: 100%; width: 100%;
    background: #0f0;
    background: linear-gradient(0deg, #01af01, #00ff2c);
}

.eqbar {
    /*position: fixed; bottom: 0; left: 0;*/
    position: relative;
    display: inline-block; height: 60px; width: 7px;
    background: #525793;
    background: linear-gradient(270deg, #2d3a7e, #4d2f95);
    border-top: 1px solid #2f303c;
    border-left: 1px solid #2f303c;
    border-right: 1px solid #747cd3;
    border-bottom: 1px solid #747cd3;
}
.eqlevel {
    position: absolute; bottom: 0;
    display: block; height: 100%; width: 100%;
    background: #0f0;
    background: linear-gradient(270deg, #01af01, #00ff2c);
}
#eq0 { height: 50%; }
#eq1 { height: 50%; }
#eq2 { height: 50%; }
#eq3 { height: 50%; }
#eq4 { height: 50%; }
#eq5 { height: 50%; }
#eq6 { height: 50%; }
#eq7 { height: 50%; }
#eq8 { height: 50%; }
#eq9 { height: 50%; }

#loliadd {
    position: fixed;
    top: 0;
    right: 0;
    z-index: 10025;
    width: 32px;
    height: 36px; 
    background: transparent;
    /*box-shadow: 0 0 5px #621F33*/;
    opacity: 0.8;
    cursor: pointer;
    transition: background .5s ease, right 1s;
}
#loli_frm {
    position: fixed; top: 0; left: 0;
    z-index: 10024;
    width: 100%; height: 100%;
    pointer-events: none;
    background: transparent;
}
.lolion { background: rgba(255,124,197,.3) !important; border-bottom-left-radius: 12px; }
.lolion.blue { background: rgba(124,105,255,.3) !important; border-bottom-left-radius: 12px; }
.lolion.grey { background: rgba(97,97,97,.3) !important; border-bottom-left-radius: 12px; }
.lolion.black { background: rgba(0,0,0,.3) !important; border-bottom-left-radius: 36px; }
.lolion.dblue { background: rgba(26,0,176,.3) !important; border-bottom-left-radius: 36px; }
.lolion.dpink { background: rgba(163,11,81,.3) !important; border-bottom-left-radius: 36px; }

#displayMini_cont {
    position: fixed; top: 28px; left: 28px;
    width: 142px; height: 82px;
    display: none;
    z-index: 10025;
}
#display, #displayMini {
    display: block;
    background: #1f1f1f;
    border: 1px solid #363; border-radius: 2px;
    width:100%; height: 100%;
    position: relative;
}
#displayMini { 
    border-radius: 10px;
    background: transparent;
    user-select: none;
}
#time, #timeMini {
    font-size: 22pt;
    font-family: monospace;
    color: #0f0; /*background: #333;*/
    float: right;
    padding: 4px;
    position: relative; z-index: 100;
}
#title {
    background: #06070a;
    color: #0f0;
    border: 1px solid #026; border-radius: 2px;
    /*position: relative;*/
}
#inner {
    display: inline-block;
}
#visualizer, #visualizerMini {
    background: transparent;
    width: 100%; height: 69%;
    position: absolute; bottom: 0;
    z-index: 1;
}
#visualizerMini {
    border-radius: 10px;
}

#spaceship {
    position: fixed; top: 0; left: 0;
    width: 100%; height: 100%;
    /*background: url('./spaceship.png') no-repeat center center transparent;
    background-size: cover;*/
    background: transparent;
    visibility: hidden;
    opacity: 0; /* start hidden - preload async */
    z-index: 5;
    /*filter: sepia(.8) brightness(2) contrast(1.2);*/ /* sunshine */
    /*filter: brightness(.8) contrast(1.1);*/ /* dimlights */
}
#spaceship_sun {
    position: fixed; top: 0; left: 0;
    width: 100%; height: 100%;
    /*background: url('./spaceship.png') no-repeat center center transparent;
    background-size: cover;*/
    background: transparent;
    visibility: hidden;
    opacity: 0; /* start hidden - preload async */
    z-index: 6;
    /*filter: sepia(.8) brightness(2) contrast(1.2);*/ /* sunshine */
    /*filter: brightness(.8) contrast(1.1);*/ /* dimlights */

    /* do these transition steps with js with theta from mg1 camera orbit
    /* light from right to left - entering light mode */
    /* fully dark */
    mask-image: linear-gradient(90deg, transparent 100%, black 110%);
    -webkit-mask-image: linear-gradient(90deg, transparent 100%, black 110%);
    /* fully light */
    /*mask-image: linear-gradient(90deg, transparent -10%, black 0%);
    -webkit-mask-image: linear-gradient(90deg, transparent -10%, black 0%);*/

    /* light from left to right - leaving light mode */
    /* fully light */
    /*mask-image: linear-gradient(90deg, black 100%, transparent 110%);
    -webkit-mask-image: linear-gradient(90deg, black 100%, transparent 110%);*/
    /* fully dark */
    /*mask-image: linear-gradient(90deg, black -10%, transparent 0%);
    -webkit-mask-image: linear-gradient(90deg, black -10%, transparent 0%);*/
    display: none;
}

.inverted-mask {
    /* Add this for the hole */
  clip-path: polygon(
    evenodd,
    /* Outer path: full div (clockwise) */
    0% 0%, 100% 0%, 100% 100%, 0% 100%, 0% 0%,
    /* Inner hole path: top 50%, left 10%, to right 100%, bottom 100% (counter-clockwise for subtraction) */
    8.6% 71.6%, 8.6% 100%, 100% 100%, 100% 71.6%, 8.6% 71.6%
  );
}

#christmastreeship {
    position: absolute; top: 0; left: 0;
    width: 100%; height: 100%;
    /*background: url('./spaceship.png') no-repeat center center transparent;
    background-size: cover;*/
    background: transparent;
    display: none;
    z-index: 8;
}
#christmastreeship_sun {
    position: absolute; top: 0; left: 0;
    width: 100%; height: 100%;
    /*background: url('./spaceship.png') no-repeat center center transparent;
    background-size: cover;*/
    background: transparent;
    mask-image: linear-gradient(90deg, transparent 100%, black 110%);
    -webkit-mask-image: linear-gradient(90deg, transparent 100%, black 110%);
    display: none;
    z-index: 9;
}
#fo-cont {
    position: absolute;
    /*top: 200px; left: 50%; margin-left: -214px;*/
    width: 100%; height: 100%;
    top: 0; left: 0;
    overflow: visible;
    pointer-events: none;
    image-rendering: pixelated;
    display: none;
    opacity: 0; /* start hidden - preload async */
    z-index: 4999;
}
#flames-overlay {
    position: absolute;
    top: 0; left: 0;
    height: 100%;
    /*top: 0; left: 0;*/
    /*top: 200px; left: 50%; margin-left: -214px;*/
    /*width: 100%; height: 100%;*/
    /*inset: 0;*/
    z-index: 10;
}

.lnkbtn {
    padding: 8px;
    border: 1px solid #aaa;
    border-radius: 10px;
}
/*#btn_ship { margin-right: 28px; }
#btn_dir { margin-left: 28px; }*/
#btn_ship { position: fixed; bottom: 28px; right: 28px; }
#btn_full { position: relative; top: 22px; border: 1px solid transparent; transition: border 1s ease-in; }
#btn_dir  { position: fixed; bottom: 28px; left: 28px; }
#btn_fot  { position: fixed; bottom: 28px; left: 50%; margin-left: -150px; border: 1px solid transparent; transition: border 1s ease-in; }
#footer_c, #title_c {
    /*visibility: visible;*/
    opacity: 1;
    transition: opacity 1s ease-out;
    user-select: none;
}
#footer_swap, #title_swap {
    /*visibility: visible;*/
    opacity: 0;
    transition: opacity 1s ease-in;
    user-select: none;
}
#btn_fot:hover #footer_c, #btn_fot:active #footer_c { opacity: 0; }
#btn_fot:hover #footer_swap, #btn_fot:active #footer_swap { opacity: 1; }
#btn_fot:hover { border: 1px solid #aaa; }

#btn_full:hover #title_c, #btn_full:active #title_c { opacity: 0; }
#btn_full:hover #title_swap, #btn_full:active #title_swap { opacity: 1; }
#btn_full:hover { border: 1px solid #aaa; }

#mg1_tim { 
    display: none;
    position: fixed;
    left: 28px;
    bottom: 28px;
    color: #666;
}

#listfrm {
    /*max-height: 256px;*/
    height: 100%;
    overflow-x: hidden;
    overflow-y: auto;
    /*resize: vertical;*/
}
#resizeFrm {
    height: 184px;
    min-height: 90px;
    /*min-width: 600px;*/
    min-width: 240px;
    position: relative;
}
.custom-resize-handle {
  width: 10px;
  height: 10px;
  background-color: transparent;
    border-left: 5px solid transparent;
    /*border-top: 5px solid #0f0;*/
    border-bottom: 5px solid #525793;
  cursor: nwse-resize;
  position: absolute;
  bottom: 0;
  right: 0;
}
</style>

<!--<script type="text/javascript" src="./js/main.js?v=1"></script>-->
<script type="text/javascript">
var $=function(id) { return document.getElementById(id); };
var lerp=function(a,b,t) { return a + t * ( b - a ) };
var clamp=function(val,min,max,) { return Math.min(Math.max(val,min), max) };

var playing=false;
var idxPlaying=1;
var idxText="1. ";

var volume=100;
var idxCurrentTime=0;
var idxDuration=0;

function playpause() {
    if(!playing) {
        play(idxPlaying);
    } else {
        clearplay();
    }
}
function prev() {
    var elms=$('player').getElementsByClassName('playing');
    if(loopon) {
        play(idxPlaying);
        return;
    }

    if(idxCurrentTime>1) {
        // rewind song
        play(idxPlaying);
    } else {
        if(idxPlaying-1 <= 0) {
            // rewind list
            play(elms.length);
        } else {
            // prev
            play(idxPlaying-1);
        }
    }
}

function rndMinMax(min,max) { // min and max included
  return Math.floor(Math.random()*(max-min+1)+min);
}
function next() {
    var elms=$('player').getElementsByClassName('playing');
    if(loopon) {
        play(idxPlaying);
        return;
    }

    if(idxPlaying-1 >= elms.length-1) {
        // rewind
        play(1);
    } else {
        // next
        play(idxPlaying+1);
    }
}
var shufindexes=[];
function rearrangelist(firstload=0) {
    var tracks=[];
    var cidxPlaying=localtracks[0];
    if(shufon) {
        cidxPlaying=localtracks[((idxPlaying-1)*2)]; // multidim array
        //console.log("from normal cidxPlaying: Track"+cidxPlaying);
        shufindexes=[]; // clear

        var tmptracks=JSON.parse(JSON.stringify(localtracks)); // make copy
        var maxtries=100+tmptracks.length;
        var rndnum=0;
        while(tmptracks.length>0 && maxtries>0) {
            if(tmptracks.length>1) {
                rndnum=rndMinMax(0,Math.floor((tmptracks.length-1)/2)); // -1 is vital
                rndnum*=2; // multidim array
            } else {
                rndnum=0;
            }
            tracks.push(tmptracks[rndnum]);
            tracks.push(tmptracks[(rndnum+1)]);
            if(tmptracks[rndnum]==cidxPlaying) {
                idxPlaying=Math.floor(tracks.length/2); // multidim arr
            }
            //console.log("push "+tmptracks[rndnum]);
            //console.log("push "+tmptracks[(rndnum+1)]);
            //console.log("splice "+rndnum+" "+(rndnum+1));
            shufindexes.push(tmptracks[rndnum]);
            tmptracks.splice(rndnum,2); // multidim array
            maxtries--;
        }
        if(maxtries==0) {
            console.log("rearrange list shufon maxtries exceeded");
        }
    } else {
        tracks=localtracks; // ref is fine

        cidxPlaying=shufindexes[(idxPlaying-1)]; // multidim array
        //console.log("from shuffle cidxPlaying: Track"+cidxPlaying);

        for(let i=0;i<tracks.length;i+=2) {
            if(tracks[i]==cidxPlaying) {
                idxPlaying=Math.floor(i/2)+1; // multidim arr
            }
        }
    }
    //console.dir(tracks);

    $('listfrm').innerHTML="";
    var c=0;
    var playmins=0;
    var playsecs=0;
    var maxtry=0;
    var html="";
    for(var i=0;i<tracks.length;i+=2) {
        c++;
        let t=tracks[i];
        let secs=tracks[i+1];
        playsecs+=secs;
        var playingstr="";
        //if(c==1) {
        //    playingstr=" selected";
        //}
        maxtry=1000;
        var mins=0;
        while(secs>=60 && maxtry>0) {
            secs-=60;
            mins++;
            maxtry--;
            //console.log('maxtry1: '+maxtry);
        }
    
        html="<a href=\"javascript:play("+c+");\" class=\"track\" id=\"aud"+c+"\">";
        html+="<div class=\"tbl\">";
        html+="<div class=\"tr\">";
        html+="<div class=\"td\" style=\"width: 24px;\">";
        html+="<span class=\"playing"+playingstr+"\">";
        html+="</div>";
        html+="<div class=\"td\" style=\"width: 48px;\">";
        html+="#"+c+"";
        html+="</div>";
        html+="<div class=\"td\">";
        html+="Track"+t+"";
        html+="</div>";
        html+="<div class=\"td\" style=\"width: 50px;\">";
        html+=""+mins+":"+secs+"";
        html+="</div>\n";
        html+="</div>";
        html+="</div>";
        html+="<input type=\"hidden\" class=\"mp3name\" value=\""+t+"\" />";
        html+="</a>\n\n";
        $('listfrm').insertAdjacentHTML('beforeend',html);
    }

    if(firstload==1) {
        maxtry=1000;
        while(playsecs>60 && maxtry>0) {
            playmins++;
            playsecs-=60;
            maxtry--;
            //console.log('maxtry2: '+maxtry);
        }
        $('runtime').innerHTML="";
        $('runtime').insertAdjacentHTML('beforeend',playmins+" min "+playsecs+" sec");
    } else {
        $('lcd_frm').contentWindow.postMessage(['new_text',idxPlaying+'. Track'+t],'*');
    }

    if(playing) {
        highlightplaying(idxPlaying);
        //console.log(idxPlaying);
    }
}
var shufon=false;
function shuftog() {
    shufon=!shufon;

    if(shufon) {
        if(loopon) {
            looptog();
        }
        $('lbshuf').style.display="inline-block";
        $('btnshuftog').className="btn tog";
    } else {
        $('lbshuf').style.display="none";
        $('btnshuftog').className="btn";
    }
    
    rearrangelist();
}
var loopon=false;
function looptog() {
    loopon=!loopon;

    if(loopon) {
        if(shufon) {
            shuftog();
        }
        $('lbloop').style.display="inline-block";
        $('btnlooptog').className="btn tog";
    } else {
        $('lbloop').style.display="none";
        $('btnlooptog').className="btn";
    }
}
var eqon=false;
function eqtog() {
    eqon=!eqon;

    if(eqon) {
        $('btneqtog').className="btn tog";
        $('eqfrm').style.display="block";
        $('eqbrd').style.border="";
    } else {
        $('btneqtog').className="btn";
        $('eqfrm').style.display="none";
        $('eqbrd').style.border="none";
    }
}
var liston=false;
function listtog() {
    liston=!liston;

    if(liston) {
        $('btnlisttog').className="btn tog";
        $('listfrm').style.display="block";
        $('listbrd').style.border="";
        if(resizableElement.getAttribute('data-height')) {
            resizableElement.style.height=resizableElement.getAttribute('data-height')+"px";
        } else {
            resizableElement.style.height="";
        }
        resizableElement.style.minHeight="";
        //resizeHandle.style.display="";
        $('aud'+idxPlaying).scrollIntoView({behavior: 'smooth',block: 'nearest'});
    } else {
        $('btnlisttog').className="btn";
        $('listfrm').style.display="none";
        $('listbrd').style.border="none";
        resizableElement.style.height="0";
        resizableElement.style.minHeight="0";
        //resizeHandle.style.display="none";
    }
}

// set colors here
var mColor=[ // [color1],[color2] = [inner],[outer]
    //[255,0,0],[0,255,0], // red,green 
    //[255,255,0],[255,0,255], // yellow,pink
    //[0,255,255],[0,0,255] // cyan,blue
    [165,49,83],[44,53,70], // stage0 red,blue
    [56,164,83],[75,75,25], // stage1 green,yellow
    [26,72,68],[143,49,165] // stage2 cyan,violet
];

var mStage=0;
var mLapse=0; // 0-100, once 100 mStage++ or reset
var color1=[0,0,0];
var color2=[0,0,0];
var pos0=0;
var pos1=1;
var alpha=0.0;
var wW,wH,wH2,bH,bH2;
var bgAspect=15/10; // 16/9

var speed=1; // percentage per tick
function animloop() {
    mLapse+=speed;
    if(mLapse>=100) {
        mLapse=0;
        mStage++;

        if(mStage>2) mStage=0;
        //console.log("new stage = "+mStage);
    } 
    pos0=mStage*2; // skip every other for multi array
    pos1=pos0+2;
    alpha=mLapse/100;
    if(pos1>2*2) pos1=0;

    //console.log("pos0:"+pos0+", pos1:"+pos1);

    color1[0]=lerp(mColor[pos0][0],mColor[pos1][0],alpha);
    color1[1]=lerp(mColor[pos0][1],mColor[pos1][1],alpha);
    color1[2]=lerp(mColor[pos0][2],mColor[pos1][2],alpha);

    color2[0]=lerp(mColor[pos0+1][0],mColor[pos1+1][0],alpha);
    color2[1]=lerp(mColor[pos0+1][1],mColor[pos1+1][1],alpha);
    color2[2]=lerp(mColor[pos0+1][2],mColor[pos1+1][2],alpha);
    
    $('backlay').style.background="radial-gradient(ellipse at 50%, rgba("+color1.toString()+",0.25), rgba("+color2.toString()+",.25) 50%, transparent 100%)";
}

var kAT;
var fps=12;
var switchNext=-1; // automatic for end of song unless loop on
var updateTime=0;
var mgLightValSet=0;
var mgLightTunSet=0;
var playerGlow=0;
var playerGlowVal=0;
var pglowc;
var maxgrad=50; // 10
function keepAlive() {
    clearTimeout(kAT);

    if(playing) {
        idxCurrentTime=$('myAudio').currentTime;
        //idxDuration//
        //console.log(idxCurrentTime);
        // seekpos = percentage * duration / 100 = time
        var iPercent = idxCurrentTime*100/idxDuration;
        iPercent=clamp(iPercent,0,100);
        $('progress').style.width=iPercent+"%";

        if(switchNext>-1) {
            switchNext--;
            //console.log("ready switch "+switchNext);
            if(switchNext==0) {
                // switch now
                switchNext=-1;
                //console.log("switching next done")
                next();
            }
        } else if((idxDuration - idxCurrentTime <= 1) && !loopon) {
            switchNext=fps;
        }

        if(updateTime<=0) {
            var cmin=0;
            var csec=parseInt(idxCurrentTime);
            while(csec>=60) {
                csec-=60;
                cmin++;
            }
            var strcsec=(csec<10)?"0"+csec:csec;
            var strcmin=(cmin<10)?"0"+cmin:cmin;

            $('time').innerHTML=strcmin+":"+strcsec;
            $('timeMini').innerHTML=strcmin+":"+strcsec;
            updateTime=fps; // 1 per fps
        } else {
            updateTime--;
        }

        //$('visualizer').width = $('visualizer').clientWidth;
        //$('visualizer').height = $('visualizer').clientHeight;
    }
    
    animloop(); // always update bg colors

    if(shipOn && shipOpa<100) { // fade in
        let sval=(100-shipOpa)*.05; // inverted
        if(sval<0.01) sval=0.01;
        shipOpa+=sval; // default +3
        if(shipOpa>=100) {
            shipOpa=100;
        }
        if(lastShipOpa==0) {
            $('spaceship').style.visibility="visible";
            $('spaceship_sun').style.visibility="visible";
            $('lcd_cont').style.visibility="visible";
            $('fo-cont').style.visibility="visible";
        }
    } else if(!shipOn && shipOpa>0) { // fade out
        let sval=shipOpa*.05;
        if(sval<0.01) sval=0.01;
        shipOpa-=sval; // default -3
        if(shipOpa<=0) {
            shipOpa=0;
            $('spaceship').style.visibility="hidden";
            $('spaceship_sun').style.visibility="hidden";
            $('lcd_cont').style.visibility="hidden";
            $('fo-cont').style.visibility="hidden";
        }
    }
    if(lastShipOpa!=shipOpa) { 
        $('spaceship').style.opacity=(shipOpa/100);
        $('spaceship_sun').style.opacity=(shipOpa/100);
        $('lcd_cont').style.opacity=(shipOpa/100);
        if(isChristmas) {
            $('fo-cont').style.opacity=(shipOpa/100);
        }
        lastShipOpa=shipOpa;
    }

    //console.log(mgLightVal+" "+mgLightValSet);
    if(!mgLightOn && mgLightVal>0 && mgLightValSet<1) { // mglightmode1?
        mgLightMode=1;
        mgLightValSet=Math.abs(mgLightVal);
        let mlv=((mgLightValSet)-.1)*(100+maxgrad);
        let mlvo=mlv+maxgrad;
        let transcolor="rgba(0,0,0,"+(mgLightValSet)+")";
        $('spaceship_sun').style.maskImage="linear-gradient(90deg, black "+mlv+"%, "+transcolor+" "+mlvo+"%)";
        $('spaceship_sun').style.webkitMaskImage="linear-gradient(90deg, black "+mlv+"%, "+transcolor+" "+mlvo+"%)";
        if(isChristmas) {
            $('christmastreeship_sun').style.maskImage="linear-gradient(90deg, black "+mlv+"%, "+transcolor+" "+mlvo+"%)";
            $('christmastreeship_sun').style.webkitMaskImage="linear-gradient(90deg, black "+mlv+"%, "+transcolor+" "+mlvo+"%)";
        }
        //
        let mlvi=((1-mgLightValSet)-.1)*(100+maxgrad);
        let mlvio=mlvi+maxgrad;
        transcolor="rgba(0,0,0,"+(1-mgLightValSet)+")";
        $('spaceship').style.maskImage="linear-gradient(-90deg, black "+mlvi+"%, "+transcolor+" "+mlvio+"%)";
        $('spaceship').style.webkitMaskImage="linear-gradient(-90deg, black "+mlvi+"%, "+transcolor+" "+mlvio+"%)";
        if(isChristmas) {
            $('christmastreeship').style.maskImage="linear-gradient(-90deg, black "+mlvi+"%, "+transcolor+" "+mlvio+"%)";
            $('christmastreeship').style.webkitMaskImage="linear-gradient(-90deg, black "+mlvi+"%, "+transcolor+" "+mlvio+"%)";
        }
        if(mgLightValSet>=1) {
            mgLightOn=true;
        }
        //console.log("Turning light on mlv: "+mlv+", mlvo: "+mlvo);
    } else if((mgLightOn || mgLightMode==1) && mgLightVal<1 && mgLightValSet>0) { // mglightmode2?
        mgLightMode=2;
        mgLightValSet=Math.abs(mgLightVal);
        let mlv=((mgLightValSet)-.1)*(100+maxgrad);
        mlv-=30;
        let mlvo=mlv+maxgrad;
        let transcolor="rgba(0,0,0,"+(mgLightValSet)+")";
        $('spaceship_sun').style.maskImage="linear-gradient(-90deg, black "+mlv+"%, "+transcolor+" "+mlvo+"%)";
        $('spaceship_sun').style.webkitMaskImage="linear-gradient(-90deg, black "+mlv+"%, "+transcolor+" "+mlvo+"%)";
        if(isChristmas) {
            $('christmastreeship_sun').style.maskImage="linear-gradient(-90deg, black "+mlv+"%, "+transcolor+" "+mlvo+"%)";
            $('christmastreeship_sun').style.webkitMaskImage="linear-gradient(-90deg, black "+mlv+"%, "+transcolor+" "+mlvo+"%)";
        }
        //
        let mlvi=((1-mgLightValSet)-.1)*(100+maxgrad);
        let mlvio=mlvi+maxgrad;
        transcolor="rgba(0,0,0,"+(1-mgLightValSet)+")";
        $('spaceship').style.maskImage="linear-gradient(90deg, black "+mlvi+"%, "+transcolor+" "+mlvio+"%)";
        $('spaceship').style.webkitMaskImage="linear-gradient(90deg, black "+mlvi+"%, "+transcolor+" "+mlvio+"%)";
        if(isChristmas) {
            $('christmastreeship').style.maskImage="linear-gradient(90deg, black "+mlvi+"%, "+transcolor+" "+mlvio+"%)";
            $('christmastreeship').style.webkitMaskImage="linear-gradient(90deg, black "+mlvi+"%, "+transcolor+" "+mlvio+"%)";
        }
        if(mgLightValSet<=0) {
            mgLightOn=false;
        }
        //console.log("Turning light off mlv: "+mlv+", mlvo: "+mlvo);
    } else if(mgLightOn && mgLightTun>mgLightTunSet && mgLightTun!=1) { // eclipse in mglightmode=3?
        mgLightMode=3; 
        mgLightTunSet=Math.abs(mgLightTun);
        let mlv=((1-mgLightTunSet)-.1)*(100+maxgrad);
        let mlvo=mlv+maxgrad;
        let transcolor="rgba(0,0,0,"+(mgLightTunSet)+")";
        $('spaceship_sun').style.maskImage="linear-gradient(90deg, "+transcolor+" "+mlv+"%, black "+mlvo+"%)";
        $('spaceship_sun').style.webkitMaskImage="linear-gradient(90deg, "+transcolor+" "+mlv+"%, black "+mlvo+"%)";
        if(isChristmas) {
            $('christmastreeship_sun').style.maskImage="linear-gradient(90deg, "+transcolor+" "+mlv+"%, black "+mlvo+"%)";
            $('christmastreeship_sun').style.webkitMaskImage="linear-gradient(90deg, "+transcolor+" "+mlv+"%, black "+mlvo+"%)";
        }
        //
        let mlvi=((mgLightTunSet)-.1)*(100+maxgrad);
        let mlvio=mlvi+maxgrad;
        transcolor="rgba(0,0,0,"+(1-(mgLightTunSet*.9))+")";
        $('spaceship').style.maskImage="linear-gradient(-90deg, "+transcolor+" "+mlvi+"%, black "+mlvio+"%)";
        $('spaceship').style.webkitMaskImage="linear-gradient(-90deg, "+transcolor+" "+mlvi+"%, black "+mlvio+"%)";
        if(isChristmas) {
            $('christmastreeship').style.maskImage="linear-gradient(-90deg, "+transcolor+" "+mlvi+"%, black "+mlvio+"%)";
            $('christmastreeship').style.webkitMaskImage="linear-gradient(-90deg, "+transcolor+" "+mlvi+"%, black "+mlvio+"%)";
        }
        //console.log("eclipse out: mlv="+mlv+", mlvo="+mlvo);
        
    } else if(mgLightOn && mgLightTun<mgLightTunSet) { // eclipse out mglightmode=4?
        mgLightMode=4;
        mgLightTunSet=Math.abs(mgLightTun);
        let mlv=((mgLightTunSet)-.1)*(100+maxgrad);
        mlv-=30;
        let mlvo=mlv+maxgrad;
        let transcolor="rgba(0,0,0,"+(mgLightTunSet)+")";
        $('spaceship_sun').style.maskImage="linear-gradient(90deg, black "+mlv+"%, "+transcolor+" "+mlvo+"%)";
        $('spaceship_sun').style.webkitMaskImage="linear-gradient(90deg, black "+mlv+"%, "+transcolor+" "+mlvo+"%)";
        if(isChristmas) {
            $('christmastreeship_sun').style.maskImage="linear-gradient(90deg, black "+mlv+"%, "+transcolor+" "+mlvo+"%)";
            $('christmastreeship_sun').style.webkitMaskImage="linear-gradient(90deg, black "+mlv+"%, "+transcolor+" "+mlvo+"%)";
        }
        //
        let mlvi=((1-mgLightTunSet)-.1)*(100+maxgrad);
        let mlvio=mlvi+maxgrad;
        transcolor="rgba(0,0,0,"+(1-mgLightTunSet)+")";
        $('spaceship').style.maskImage="linear-gradient(-90deg, black "+mlvi+"%, "+transcolor+" "+mlvio+"%)";
        $('spaceship').style.webkitMaskImage="linear-gradient(-90deg, black "+mlvi+"%, "+transcolor+" "+mlvio+"%)";
        if(isChristmas) {
            $('christmastreeship').style.maskImage="linear-gradient(-90deg, black "+mlvi+"%, "+transcolor+" "+mlvio+"%)";
            $('christmastreeship').style.webkitMaskImage="linear-gradient(-90deg, black "+mlvi+"%, "+transcolor+" "+mlvio+"%)";
        }
        //console.log("eclipse in: mlv="+mlv+", mlvo="+mlvo);
    }

    if(playerGlow==0) {
        playerGlowVal+=.05;
        if(playerGlowVal>=1.0) {
            playerGlow=1;
            playerGlowVal=1.0;
        }
    } else {
        playerGlowVal-=.05;
        if(playerGlowVal<=0.0) {
            playerGlow=0;
            playerGlowVal=0.0;
        }
    }
    $('player').style.boxShadow="0 0 "+(10+(60*playerGlowVal))+"px #00f9cb36";
    //pglowc="rgba("+(0*playerGlowVal)+","+((249*playerGlowVal)+100)+","+((203*playerGlowVal)+100)+",.54)";
    //pglowc="rgba(0,249,203,."+parseFloat(playerGlowVal).toFixed(2)+")";
    //$('player').style.boxShadow="0 0 600px "+pglowc;
    //console.log(playerGlowVal+" - "+playerGlow);

    if(!backlayHide && backlayOpa<100) { // fade in
        let sval=(100-backlayOpa)*.05; // inverted
        if(sval<0.01) sval=0.01;
        backlayOpa+=sval; // default +3
        if(backlayOpa>=100) {
            backlayOpa=100;
        }
        if(lastBacklayOpa==0) {
            $('backlay').style.visibility="visible";
        }
    } else if(backlayHide && backlayOpa>0) { // fade out
        let sval=backlayOpa*.05;
        if(sval<0.01) sval=0.01;
        backlayOpa-=sval; // default -3
        if(backlayOpa<=0) {
            backlayOpa=0;
            $('backlay').style.visibility="hidden";
        }
    }
    if(lastBacklayOpa!=backlayOpa) {
        $('backlay').style.opacity=(backlayOpa/100);
        lastBacklayOpa=backlayOpa;
    }

    kAT=setTimeout(function() { keepAlive(); },1000/fps);
}

function highlightplaying(idx) {
    var elms=$('player').getElementsByClassName('playing');
    for(var i=0;i<elms.length;i++) {
        var c=i+1;
        if(c==idx) {
            elms[i].className="playing selected";
            $('aud'+c).className="track selected";
            idxText=c+". "+$('aud'+c).getElementsByClassName('td')[2].innerHTML; // idx|name
            t=$('aud'+c).getElementsByClassName('mp3name')[0].value;

            elms[i].scrollIntoView({behavior: 'smooth',block: 'nearest'});
        } else {
            elms[i].className="playing";
            $('aud'+c).className="track";
        }
    }
}

var cache=36;
var t="";
function play(idx) {
    //alert(idx);
    var reloading=true;
    if(idx==idxPlaying && !playing) {
        reloading=false;
    } else {
        switchNext=-1;
    }
    idxPlaying=idx;

    highlightplaying(idx);

    playing=true;
    $('btnplaypause').value="∎";
    $('triangle').className="triangle on";
    $('triangleMini').className="triangleMini on";

    if(reloading) {
        //$('myAudio').src=idx+".mp3"; // need t, not c
        $('myAudio').src=t+".mp3?c="+cache;
    }
    $('inner').innerHTML=idxText;
    let dur=$('aud'+idx).getElementsByClassName('td')[3].innerHTML;
    $('lcd_frm').contentWindow.postMessage(['new_text',idxText+" ("+dur+")"],'*');
    $('myAudio').play();

    if(!audioContextInitiated) {
        createAudioContext();
    }

    if(eqloaded==1 || eqtrig==1) {
        updateeq();
        eqloaded=2;
    }
}
var initClear=0;
function clearplay() {
    var elms=$('player').getElementsByClassName('playing');
    for(var i=0;i<elms.length;i++) {
        var c=i+1;
        if(c==idxPlaying) {
            elms[i].className="playing"; // not playing
            $('aud'+c).className="track selected";
            idxText=c+". "+$('aud'+c).getElementsByClassName('td')[2].innerHTML; // idx|name
            t=$('aud'+c).getElementsByClassName('mp3name')[0].value;
        } else {
            elms[i].className="playing";
            $('aud'+c).className="track";
        }
    }

    playing=false;
    $('btnplaypause').value="►";
    $('triangle').className="triangle";
    $('triangleMini').className="triangleMini";

    if(initClear==0) {
        $('myAudio').src=t+".mp3?c="+cache;
        initClear=1;
    }
    $('inner').innerHTML=idxText;
    //let dur=$('aud'+idxPlaying).getElementsByClassName('td')[3].innerHTML;
    //$('lcd_frm').contentWindow.postMessage(['new_text',idxText+" ("+dur+")"],'*');
    $('lcd_frm').contentWindow.postMessage(['new_text',"Stand-by"],'*');
    $('myAudio').pause();
}

var plX=-1;
var plY=-1;
var logstr="";
var touchDetect=false;
function mousehandle(e) {
    e=event || window.event;

    var lX=e.clientX || e.targetTouches[0].pageX;
    var lY=e.clientY || e.targetTouches[0].pageY;

    //console.log("lX: "+lX+", lY: "+lY);

    var sY=window.scrollY;
    var sX=window.scrollX;

    plX=lX;
    plY=lY;
    if(touchDetect) {
        plX-=sX;
        plY-=sY;
    }

   // logstr="lX = "+lX;
   // logstr+="<br />lY = "+lY;
   // logstr+="<br />sX = "+sX;
   // logstr+="<br />sY = "+sY;
   // logstr+="<br />irX = "+irX;
   // logstr+="<br />irY = "+irY;
   // logstr+="<br />touchDetect = "+(touchDetect)?"True":"False";
   // $('log').innerHTML=logstr;

    // update on move (and hold)
    if(seeklock) {
        seekpos();
    } else if(vollock) {
        setvol();
    } else if(eqlock!=-1) {
        seteq(eqlock);
    }

    if (isResizing && irY != 0 && irX != 0) {
        let ilX=(plX-irX) * 2;
        let ilY=(plY-irY) * 2;
        //console.log(ilX+" "+ilY);
        let nh=resizableElement.clientHeight+ilY;
        let nw=resizableElement.clientWidth+ilX;
        let dw=(600-nw);
        if(liston) {
            //console.log(nh);
            //console.log(window.innerHeight);
            if(nh>(window.innerHeight-(200))) nh=window.innerHeight-(200);
            resizableElement.style.height=nh+"px";
            resizableElement.setAttribute('data-height',nh);
        }
        //if(dw>0) dw=0;
        if(nw>(window.innerWidth-((28*2)+14))) nw=window.innerWidth-((28*2)+14);
        //if(dw<0 && nw<(window.innerWidth-((28*2)+14))) {
            resizableElement.style.width=nw+"px";
            resizableElement.setAttribute('data-width',nw);
            //$('player').style.marginLeft=dw+"px";
            $('player').style.maxWidth=(602-dw)+"px";
        //}
        irX=plX;
        irY=plY;
    }
}

var seeklock=false;
var vollock=false;
var eqlock=-1;

function unlockHold() {
    seeklock=false;
    vollock=false;
    eqlock=-1;

    if (isResizing) {
        isResizing = false;
        for(let i=0;i<ifrm.length;i++) {
            ifrm[i].style.pointerEvents="";
        }
        $('listfrm').style.pointerEvents="";
        //console.log("resize off");
    }
}

function lockseek() {
    seeklock=true;

    seekpos(); // initial
}
function lockvol() {
    vollock=true;

    setvol(); // initial
}
function lockeq(x) {
    eqlock=x;
    
    seteq(eqlock); // initial
}

function seekpos() {
    var elm=$('bar').getBoundingClientRect();

    var ilX=plX - elm.left;
    var ilY=plY - elm.top;
    var iPercent = ilX * 100 / elm.width; // 0 - 100
    //alert("mouseX = "+ilX+" mouseY = "+ilY+" percent = "+iPercent);
    iPercent=clamp(iPercent,0,100);

    $('progress').style.width = iPercent+"%";

    var seekpos = iPercent * idxDuration / 100;
    $('myAudio').currentTime = seekpos;
}

function setvol() {
    var elm=$('vol').getBoundingClientRect();

    var ilX=plX - elm.left;
    var ilY=plY - elm.top;
    var iPercent = ilX * 100 / elm.width; // 0 - 100
    //alert("mouseX = "+ilX+" mouseY = "+ilY+" percent = "+iPercent);
    iPercent=clamp(iPercent,0,100);

    $('level').style.width = iPercent+"%";

    volume = parseInt(iPercent);

    $('myAudio').volume = volume/100;

    localStorage.setItem("volume",iPercent);
}

function loadvol() {
    if(localStorage.getItem("volume")) {
        var iPercent=localStorage.getItem("volume")

        $('level').style.width = iPercent+"%";

        volume = parseInt(iPercent);

        $('myAudio').volume = volume/100;
    }
}

var ryg=[
    [1,175,1],[0,255,44], // green
    [175,175,0],[255,255,0], // yellow
    [175,50,0],[255,50,0] // red
];
var rcolor1=[0,0,0];
var rcolor2=[0,0,0];
var arpha=0.0;
function coloreq(x,iPercent) {
    // lerp [0][0-2] to [2][0-2] and [1][0-2] to [3][0-2] 0-50
    // lerp [2][0-2] to [4][0-2] and [3][0-2] to [5][0-2] 50-100
    var ridx=0;
    arpha=iPercent/50;
    if(iPercent>50) {
        ridx=2;
        arpha-=1.0;
    }
    rcolor1[0]=lerp(ryg[ridx][0],ryg[ridx+2][0],arpha);
    rcolor1[1]=lerp(ryg[ridx][1],ryg[ridx+2][1],arpha);
    rcolor1[2]=lerp(ryg[ridx][2],ryg[ridx+2][2],arpha);

    rcolor2[0]=lerp(ryg[ridx+1][0],ryg[ridx+3][0],arpha);
    rcolor2[1]=lerp(ryg[ridx+1][1],ryg[ridx+3][1],arpha);
    rcolor2[2]=lerp(ryg[ridx+1][2],ryg[ridx+3][2],arpha);
    //
    $('eq'+x).style.background="linear-gradient(270deg, rgb("+rcolor1+"), rgb("+rcolor2+"))";
    //console.log("arpha = "+arpha+", ");
    //console.log("rcolor1="+rcolor1.toString()+" rcolor2="+rcolor2.toString());
}

function seteq(x) {
    var elm=$('eq'+x).parentNode.getBoundingClientRect();

    var ilX=plX - elm.left;
    var ilY=plY - elm.top;
    var iPercent = 100 - (ilY * 100 / elm.height); // 0 - 100
    //alert("mouseX = "+ilX+" mouseY = "+ilY+" percent = "+iPercent);
    iPercent=clamp(iPercent,0,100);

    $('eq'+x).style.height = iPercent+"%";

    coloreq(x,iPercent);

    // convert 0 - 100 iPercent to
    // -20 to 0 to 20 where 0 = 50
    audioFilters[x][1] = parseFloat((iPercent * .4) - 20);

    //volume = parseInt(iPercent);

    //$('myAudio').volume = volume/100;
    updateeq();
    saveeq();
}
var eqpreset=[
    [50,50,50,50,50,50,50,50,50,50], // flat 
    [42,52,58,38,50,40,30,50,46,40], // preset1
    [45,60,83,45,35,30,30,70,80,50], // preset2
    [36,45,65,80,70,70,85,80,50,44], // preset3
    [35,48,65,75,86,86,75,65,48,35], // preset4
];

function loadpreset(x) {
    loadeq(x);
}

var eqloaded=0;
var eqtrig=0;
function updateeq() {
    eqtrig=1;
    if(eqloaded==0) {
        eqloaded=1;
    } else if(eqloaded==1) {
        if(!audioContextInitiated) {
            createAudioContext();
        }
        eqloaded=2;
    } else if(eqloaded==2) {
        eqtrig=0;
        for(var i=0;i<audioFilters.length;i++) {
            audioFilters[i][2].gain.value=audioFilters[i][1];
            //console.log("update gain eq "+i+" to "+audioFilters[i][1]);
        }
    }
}

function saveeq() {
    var arr=[];
    var iPercent=0;
    for(var i=0;i<audioFilters.length;i++) {
        iPercent=(audioFilters[i][1]+20)*2.5; // realign and scale
        iPercent=Math.floor(clamp(iPercent,0,100));
        arr.push(iPercent);
        //console.log("convert "+audioFilters[i][1]+" to "+iPercent);
    }
    //console.log(JSON.stringify(arr));
    localStorage.setItem("eqdata",JSON.stringify(arr));
}

function loadeq(f=-1) {
    var arr;
    var iPercent=0;
    if(localStorage.getItem("eqdata") && f==-1) {
        var data=localStorage.getItem("eqdata")
        arr=JSON.parse(data);
    } else {
        if(f==-1) f=1; // default load

        arr=eqpreset[f];
    }
    for(var i=0;i<arr.length;i++) {
        iPercent=arr[i];
        $('eq'+i).style.height = iPercent+"%";
        
        coloreq(i,iPercent);

        audioFilters[i][1] = parseFloat((iPercent * .4) - 20);
        //console.log("loading eq"+i+" = "+iPercent);
    }

    if(eqloaded==1 && f==-1) {
        eqloaded=1;
    } else {
        updateeq();
        saveeq();
    }
}

function dl() {
    //alert(idxPlaying+".mp3");
    downloadResource('//twily.info/s/firefox/mp3/'+t+".mp3","twily-album1-track"+idxPlaying+".mp3");
}

function forceDownload(blob, filename) {
  var a = document.createElement('a');
  a.download = filename;
  a.href = blob;
  // For Firefox https://stackoverflow.com/a/32226068
  document.body.appendChild(a);
  a.click();
  a.remove();
}

// Current blob size limit is around 500MB for browsers
function downloadResource(url, filename) {
  if (!filename) filename = url.split('\\').pop().split('/').pop();
  fetch(url, {
      headers: new Headers({
        'Origin': location.origin
      }),
      mode: 'cors'
    })
    .then(response => response.blob())
    .then(blob => {
      let blobUrl = window.URL.createObjectURL(blob);
      forceDownload(blobUrl, filename);
    })
    .catch(e => console.error(e));
}

var audioFilters=[ // hz,db,obj
    [60,0.0,null],
    [170,0.0,null],
    [310,0.0,null],
    [600,0.0,null],
    [1000,0.0,null],
    [3000,0.0,null],
    [6000,0.0,null],
    [12000,0.0,null],
    [14000,0.0,null],
    [16000,0.0,null]
];

var audioContextInitiated=false;
var audioContext, source, analyser, bufferLength, dataArray;
var canvas={},canvasCtx={};
function createAudioContext() {
    audioContextInitiated=true;

    audioContext = new (window.AudioContext || window.webkitAudioContext)();
    
    // Create a source node from the audio element
    source = audioContext.createMediaElementSource($('myAudio'));
    
    // Create an AnalyserNode for audio analysis
    analyser = audioContext.createAnalyser();
    analyser.fftSize = 128; // Number of samples for FFT (higher = more detail)
    bufferLength = analyser.frequencyBinCount; // Half of fftSize
    dataArray = new Uint8Array(bufferLength); // Array to store frequency data
    
    for(var i=0;i<audioFilters.length;i++) {
        audioFilters[i][2] = audioContext.createBiquadFilter();
        var type="lowshelf";
        if(i>6) {
            type="highshelf";
        } else if(i>3) {
            type="peaking";
        }
        audioFilters[i][2].type=type;
        audioFilters[i][2].frequency.value=audioFilters[i][0];
    }
    updateeq();
    
    // Connect the audio source to the analyser and then to the output (speakers)
    source.connect(analyser);

    analyser.connect(audioFilters[0][2]);
    for(var i=0;i<audioFilters.length-1;i++) {
        audioFilters[i][2].connect(audioFilters[(i+1)][2]);
    }
    //analyser.connect(audioContext.destination);
    audioFilters[9][2].connect(audioContext.destination);

    canvas['norm'] = $('visualizer');
    canvas['mini'] = $('visualizerMini');
    canvasCtx['norm'] = canvas['norm'].getContext("2d"); 
    canvasCtx['mini'] = canvas['mini'].getContext("2d"); 
    // Start the visualization loop
    draw();
}
var minimized="norm";

// Visualization function
var barWidth,barHeight;
var grad,red,green;
function draw() {
  // Schedule the next frame
  requestAnimationFrame(draw);

  // Get the current frequency data
  analyser.getByteFrequencyData(dataArray);

  // Clear the canvas
  canvasCtx[minimized].clearRect(0, 0, canvas[minimized].width, canvas[minimized].height);
  //console.log(canvas[minimized].width+"x"+canvas[minimized].height);

  // Calculate bar width for the spectrum
  barWidth = ((canvas[minimized].width - 3) / bufferLength) * 2.5; // -3 adjust right edge last bar
  let x = 0;

  // Draw bars for each frequency bin
  for (let i = 0; i < bufferLength; i++) {
    barHeight = dataArray[i]; // Amplitude value (0-255)

    //grad = canvasCtx[minimized].createLinearGradient(0, 0, 0, barHeight);
    grad = canvasCtx[minimized].createLinearGradient(0, canvas[minimized].height / 4, 0, canvas[minimized].height);
    red=barHeight-50;
    green=255-red;
    grad.addColorStop(0, "rgb(255,50,0)");
    grad.addColorStop(0.5, "rgb(255,255,0)");
    grad.addColorStop(1, "rgb(0,127,0)");

    //canvasCtx[minimized].fillStyle = `rgb(${barHeight + 100}, 50, 50)`; // Dynamic color
    canvasCtx[minimized].fillStyle = grad;
    canvasCtx[minimized].fillRect(x, canvas[minimized].height - barHeight / 2, barWidth, barHeight / 2);
    x += barWidth + 1; // Move to the next bar with a small gap
  }
}

function newSize() {
    wW=window.innerWidth;
    wH=window.innerHeight;

    wH2=wH/2;
    
    //bW=wW;
    bH=wW * bgAspect;

    bH2 = bH/2;
}

var sprite;
var foaspect=616/1800;
function init() {
    isChristmas=isCurrentDateBetweenNov15AndApr15();

    flameCnv=$('flames-overlay');
    flameCtx=flameCnv.getContext('2d');

    $('eqbrd').style.border="none"; // default off
    init_cresizer();
    listtog(); // default on

    window_resize();

    loliadd(0);
    
    $('lcd_frm').style.height="48px";
    $('lcd_frm').style.width="422px";
    $('lcd_frm').contentWindow.postMessage(['new_height',48],'*');
    $('lcd_frm').contentWindow.postMessage(['new_width',422],'*');
    
    $('myAudio').addEventListener('loadedmetadata',function() {
        idxDuration = $('myAudio').duration;
        //console.log(idxDuration);
    });
    idxPlaying=1;
    clearplay();

    preloadSpaceship();

    if(isChristmas) {
        preloadChristmastreeship();
        $('christmastreeship').style.display="block";
        $('fo-cont').style.display="block";

        sprite = new Image();
        sprite.src = './candleflame.png';
        sprite.onload = () => {
            console.log('spritee loaded: candleflame');
            requestAnimationFrame(animateFlames);
        };
    }

    newSize();
    keepAlive();

    loadvol();
    loadeq();
}

function isCurrentDateBetweenNov15AndApr15() {
  const currentDate = new Date();
  const currentMonth = currentDate.getMonth(); // 0-indexed (0 for January, 10 for November, 3 for April)
  const currentDay = currentDate.getDate();

  // Define the start and end month/day for the period
  const startMonth = 10; // November (0-indexed)
  const startDay = 15;
  const endMonth = 3; // April (0-indexed)
  const endDay = 15;

  // Case 1: Period spans across the year end (Nov 15 - Dec 31 AND Jan 1 - Apr 15)
  if (currentMonth > startMonth || (currentMonth === startMonth && currentDay >= startDay)) {
    // Current date is in November, December, or later in the year
    return true;
  } else if (currentMonth < endMonth || (currentMonth === endMonth && currentDay <= endDay)) {
    // Current date is in January, February, March, or April
    return true;
  } else {
    return false;
  }
}

// grok on candles https://grok.com/share/c2hhcmQtMw_ce07dece-53b3-4f2f-9533-918c17315964
const candles = [
  { x: 94, y: 976, speed: 0.9, offset: 0 },
  { x: 268, y: 905, speed: 1.1, offset: 7 },
  { x: 424, y: 960, speed: 0.8, offset: 14 },
  { x: 12, y: 1215, speed: 1.3, offset: 3 },
  { x: 335, y: 1155, speed: 1.0, offset: 19 },
  { x: 550, y: 1184, speed: 0.95, offset: 11 },
];

var fw = 64;
var fh = 64;
var cols = 5;
var totalFrames = 24; // 0–23, we skip the empty 25th cell
var flameCnv,flameCtx;
function animateFlames(time) {
  flameCtx.clearRect(0, 0, flameCnv.width, flameCnv.height);
  //flameCtx.fillStyle="#f0f";
  //flameCtx.fillRect(0, 0, flameCnv.width, flameCnv.height);
  //console.log(flameCnv.width);

  candles.forEach(candle => {
    // time-based frame with per-candle speed & start offset
    let frameIndex = (Math.floor(time * 0.001 * candle.speed) + candle.offset) % totalFrames;

    let sx = (frameIndex % cols) * fw;
    let sy = Math.floor(frameIndex / cols) * fh;

    // optional tiny random sway
    let sway = Math.sin(time * 0.002 + candle.offset) * 2;

    //let sca=window.innerHeight/1800;
    //var sca=1-((Math.max(0,(window.innerWidth/1.6)-window.innerHeight)+window.innerHeight)/616); // 1.6
    var sca=(flameCnv.height*foaspect)/616;
    sca*=.5;

    let candleX=((candle.x + sway - ((fw * sca)/2))/616); // real pixel to percent
    let candleY=((candle.y - ((fh - 10) * sca))/1800);

    candleX=candleX*(flameCnv.height*foaspect); // back to current pixel
    candleY=candleY*flameCnv.height; // back to current pixel

    flameCtx.drawImage(
      sprite,
      sx, sy, fw, fh,                     // source rect
      candleX, candleY,  // dest x,y (10 px up from wick looks nice)
      (fw * sca), (fh * sca) // dest size (or scale bigger if you want)
    );
  });

  requestAnimationFrame(animateFlames);
}

var shipOpa=0;
var lastShipOpa=0;
var shipOn=false;
var shipLoaded=false;
var shipSunLoaded=false;
var isChristmas=false; // check for novbember 15-april 15
//var christmasLoaded=false;
function toggle_direction() {
    //$('star_frm').src=$('star_frm').src;
    //$('star_frm').contentWindow.postMessage('new_dir','https://analiestar.com');
    $('star_frm').contentWindow.postMessage('new_dir','*');
    // opposite way - parent.postMessage..
}
function toggle_ship() {
    if(!shipLoaded) return;
    shipOn=!shipOn;

    if(shipOn) {
        $('btn_ship').innerHTML="[ Float through space ]";
    } else {
        $('btn_ship').innerHTML="[ Return to spaceship ]";
    }
}
async function preloadSpaceship() {
    var img=new Image();
    img.onload=function() {
        shipLoaded=true;
        $('spaceship').style.background="url("+this.src+") no-repeat center center transparent";
        $('spaceship').style.backgroundSize="cover";
        toggle_ship();
    }
    img.src="./spaceship_wide.png?cache="+cache;
}
async function preloadSpaceshipSun() {
    var img=new Image();
    img.onload=function() {
        $('spaceship_sun').style.background="url("+this.src+") no-repeat center center transparent";
        $('spaceship_sun').style.backgroundSize="cover";

        if(isChristmas) {
            preloadChristmastreeshipSun();
        }
    }
    img.src="./spaceship_sunlit_wide.png?cache="+cache;
}
async function preloadChristmastreeship() {
    //christmasLoaded=true;
    var img=new Image();
    img.onload=function() {
        shipLoaded=true;
        $('christmastreeship').style.background="url("+this.src+") no-repeat center left transparent";
        $('christmastreeship').style.backgroundSize="contain";

        flameCnv.width=($('fo-cont').clientHeight*foaspect);
        flameCnv.height=$('fo-cont').clientHeight;
        flameCnv.style.width=($('fo-cont').clientHeight*foaspect)+"px";
        flameCnv.style.height=$('fo-cont').clientHeight+"px";
    }
    img.src="./christmastreeship_wide.png?cache="+cache;
}
async function preloadChristmastreeshipSun() {
    var img=new Image();
    img.onload=function() {
        $('christmastreeship_sun').style.background="url("+this.src+") no-repeat center left transparent";
        $('christmastreeship_sun').style.backgroundSize="contain";
    }
    img.src="./christmastreeship_sunlit_wide.png?cache="+cache;
}
function setSunDir() {
    //
}

var resizableElement = null;
var resizeHandle = null;
var ifrm=[];
var isResizing = false;
var irX=0, irY=0;
function begin_resize(e,t) { // player resize
    e.preventDefault();
    if(t==2) {
        touchDetect=true;
    } else {
        touchDetect=false;
    }
    if(plX!=-1 && plY!=-1) { // this and irY != 0 check prevent infinity glitch touch
        irX=plX;
        irY=plY;
    }
    for(let i=0;i<ifrm.length;i++) {
        ifrm[i].style.pointerEvents="none";
    }
    $('listfrm').style.pointerEvents="none";
    //console.log("resize on");
    isResizing = true;
}
function init_cresizer() {
    resizableElement = $('resizeFrm');
    resizeHandle = document.querySelector('.custom-resize-handle');
    ifrm=document.getElementsByTagName('iframe');
    
    resizeHandle.addEventListener('mousedown', (e) => { begin_resize(e,1); });
    resizeHandle.addEventListener('touchstart', (e) => { begin_resize(e,2); });
}

//var sca=1;
function window_resize() {
    var biggest=Math.max(window.innerWidth,window.innerHeight);

    var aspect=window.innerWidth/window.innerHeight;
    //var sca=biggest/960; // 1
    var sca=(Math.max(0,(window.innerWidth/1.6)-window.innerHeight)+window.innerHeight)/795; // 1.6
    var invSca=1-sca;
    console.log("sca: "+sca);
    console.log("invSca: "+invSca);
    $('lcd_frm').style.transform="perspective(300px) rotateX(-3deg) scale("+sca+")";
    //$('flames-overlay').style.transform="scale("+sca+")";

    $('star_frm').contentWindow.postMessage(['new_sca',sca],'*');

    //$('lcd_frm').style.top=((biggest*.245)-(invSca*25))+"px"; // 1 ratio
    $('lcd_frm').style.top=(-25+(175*(sca)))+"px"; // 1.6 ratio
    //$('flames-overlay').style.width=($('fo-cont').clientHeight*foaspect)+"px";

    //var bcr=$('lcd_frm').getBoundingClientRect();
    //if(bcr.top<0) 
    // this section replaces 1:1 ratio scale with 1.6
    if(aspect>=1.6) { // to wide, overflow height
        $('lcd_cont').style.height=(window.innerWidth/1.6)+"px";
        $('lcd_cont').style.width=""; // 100%
        $('lcd_cont').style.top=-(((window.innerWidth/1.6)-window.innerHeight)/2)+"px";
        $('lcd_cont').style.left=""; // 0
        if(isChristmas) {
            $('fo-cont').style.height=(window.innerWidth/1.6)+"px";
            $('fo-cont').style.width=""; // 100%
            $('fo-cont').style.top=-(((window.innerWidth/1.6)-window.innerHeight)/2)+"px";
            $('fo-cont').style.left=""; // 0
        }
        //$('lcd_cont').style.border="4px solid #f03";
    } else { // to tall, overflow width
        $('lcd_cont').style.height=""; // 100%
        $('lcd_cont').style.width=(window.innerHeight*1.6)+"px";
        $('lcd_cont').style.top=""; // 0
        $('lcd_cont').style.left=-(((window.innerHeight*1.6)-window.innerWidth)/2)+"px";
        if(isChristmas) {
            $('fo-cont').style.height=""; // 100%
            $('fo-cont').style.width=(window.innerHeight*1.6)+"px";
            $('fo-cont').style.top=""; // 0
            $('fo-cont').style.left=-(((window.innerHeight*1.6)-window.innerWidth)/2)+"px";
        }
        //$('lcd_cont').style.border="4px solid #30f";
    } // 1.6 ratio section end, 1:1 is with backlay below-
    
    if(isChristmas) {
        flameCnv.width=($('fo-cont').clientHeight*foaspect);
        flameCnv.height=$('fo-cont').clientHeight;
        flameCnv.style.width=($('fo-cont').clientHeight*foaspect)+"px";
        flameCnv.style.height=$('fo-cont').clientHeight+"px";
    }

    // cover fit lcd container and backlay glow to fixed aspect~ (cover mode)
    if(biggest>window.innerHeight) { // landscape
        //$('lcd_cont').style.height=biggest+"px";
        //$('lcd_cont').style.width=""; // 100%
        $('backlay').style.height=biggest+"px";
        $('backlay').style.width=""; // 100%
        let diff=biggest-window.innerHeight;
        //$('lcd_cont').style.top=-(diff/2)+"px";
        //$('lcd_cont').style.left=""; // 0
        $('backlay').style.top=-(diff/2)+"px";
        $('backlay').style.left=""; // 0
    } else { // portrait
        //$('lcd_cont').style.height=""; // 100%
        //$('lcd_cont').style.width=biggest+"px";
        $('backlay').style.height=""; // 100%
        $('backlay').style.width=biggest+"px";
        let diff=biggest-window.innerWidth;
        //$('lcd_cont').style.top=""; // 0
        //$('lcd_cont').style.left=-(diff/2)+"px";
        $('backlay').style.top=""; // 0
        $('backlay').style.left=-(diff/2)+"px";
    }

    $('btn_fot').style.marginLeft=-($('btn_fot').clientWidth/2)+"px";
}

var rzT;
window.addEventListener('resize', (e) => { // window resize
    clearTimeout(rzT);
    window_resize();
    rzT=setTimeout(function() { newSize(); }, 100);
});

var newtsmg1=[];
var newmg1str="";
var firstMG1=0;
var mgLightVal=0;
var mgLightTun=1;
var mgLightOn=false;
var mgLightMode=1;
var rad=180/Math.PI;
window.addEventListener('message', (event) => {
    //if (event.origin === 'https://links.analiestar.com') {
        //console.log('Message from parent:', event.data[0]);
            switch(event.data[0]) {
                case "new_tim":
                    newmg1str="";
                    newtsmg1=event.data[1].split(" ");
                    for(let i=0;i<4;i++) newmg1str+=newtsmg1[i]+" ";
                    newmg1str+="<br />";
                    for(let i=4;i<newtsmg1.length;i++) newmg1str+=newtsmg1[i]+" ";
                    updateTimeMg1(newmg1str);

                    //var lilen=0.5;
                    //var limul=2;
                    var lilen=0.25;
                    var limul=4;

                    // replacing lilen and limul with rad conversions;
                    let deg=event.data[3]*rad; // 235 to 0 to -0 to -124 to 235 loop
                    // 0 deg = twilight zone enter day (ship) [10 to -10]
                    // -90 deg = eclipse noon (ship) [-80 to -100]
                    // 180 deg = twilight zone enter night (ship) [190 to 170]
                    // 90 deg aligned with sun to earth midnight (ship)
                    //console.log(event.data[3]*rad);
                    //if(event.data[3]>=0 && event.data[3]<=Math.PI-lilen) {
                    if(deg>=10 && deg<=160) {
                        //console.log("staying dark");
                        if(mgLightVal!=0) {
                            mgLightVal=0;
                        }
                    //} else if(event.data[3]<=-0 && event.data[3]>=-lilen) {
                    } else if(deg<=10 && deg>=-10) {
                        //console.log("entering light");
                        //mgLightVal=Math.abs(event.data[3]*limul);
                        mgLightVal=Math.abs((20-(deg+10))/20);
                    //} else if(event.data[3]<-lilen && event.data[3]>=-Math.PI) {
                    } else if(deg<-10 || deg>=180) {
                        //console.log("staying light");
                        if(mgLightVal!=1) {
                            mgLightVal=1;
                        }
                        //if(event.data[3]>-Math.PI/2-lilen && event.data[3]<-Math.PI/2+lilen) {
                        if(deg>-115 && deg<-65) {
                            //mgLightTun=event.data[3];
                            //mgLightTun=-(-lilen-(Math.PI/2-lilen)-event.data[3])*limul;
                            //mgLightTun=Math.abs((-lilen-(Math.PI/2-lilen)-event.data[3])*limul);
                            //mgLightTun=1-Math.abs((-lilen-(Math.PI/2-lilen)-event.data[3])*limul);
                            //mgLightTun=1-Math.abs((-(lilen*2)-(Math.PI/2-(lilen*2))-event.data[3])*(limul));
                            mgLightTun=Math.abs(((50-(deg+115))/25)-1);
                        }
                        //console.log(event.data[3]+" : "+mgLightTun); // 1 0 -1 or 1 0 1 or 0 1 0
                    //} else if(event.data[3]>=Math.PI) {
                    } else if(deg>=160) {
                        //console.log("entering dark");
                        //mgLightVal=((Math.PI-event.data[3])*limul);
                        mgLightVal=Math.abs(((deg-160))/20);
                    }
                    //console.log("deg="+deg+", mgLightVal = "+mgLightVal);

                    if(firstMG1<2) {
                        let biggest=Math.max(window.innerWidth,window.innerHeight);
                        //let sca=biggest/960; // 1
                        //let invSca=1-sca;
                        // wide 1.6 ratio solution:
                        let sca=(Math.max(0,(window.innerWidth/1.6)-window.innerHeight)+window.innerHeight)/795; // 1.6
                        let invSca=1-sca;
                        $('star_frm').contentWindow.postMessage(['new_sca',sca],'*');
                        console.log("sending new-sca mg1");
                        firstMG1++;
                    }
                    break;
                case "loli_cen":
                    if(event.data[1]==1) {
                        $('loli_frm').style.pointerEvents="auto"
                        $('loli_frm').contentWindow.postMessage(['new_plxy',plX,plY],'*');
                    } else {
                        $('loli_frm').style.pointerEvents="none"
                    }
                    break;
                case "loli_blk":
                    if(event.data[1]==1) {
                        $('loliadd').style.right="-36px";
                    } else {
                        $('loliadd').style.right="";
                    }
                    break;
                default:
            }
    //}
});

function minimize() {
    minimized="mini";
    $('player').style.display="none";
    $('displayMini_cont').style.display="inline-block";
}
function maximize() {
    minimized="norm";
    $('player').style.display="";
    $('displayMini_cont').style.display="";
    if(liston) {
        $('aud'+idxPlaying).scrollIntoView({behavior: 'smooth',block: 'nearest'});
    }
}

var lolion=0;
function loliadd(x=1) {
    let rmloli=true;
    let cname="";
    let smode=["notset",0];
    if(x!=0) {
        rmloli=false;
        lolion++;
        if(lolion>6) lolion=0;

        switch(lolion) {
            case 1:
                var lolifrm=document.createElement('iframe');
                lolifrm.id="loli_frm";
                lolifrm.src="/loli/index.php?cache="+cache;
                lolifrm.frameBorder="0";
                lolifrm.border="0";
                document.body.appendChild(lolifrm);
                cname="lolion";
                break;
            case 2:
                cname="lolion blue";
                smode=['slowmode',1];
                break;
            case 3:
                cname="lolion grey";
                smode=['slowmode',2];
                break;
            case 4:
                cname="lolion black";
                smode=['slowmode',3];
                break;
            case 5:
                cname="lolion dblue";
                smode=['slowmode',4];
                break;
            case 6:
                cname="lolion dpink";
                smode=['slowmode',5];
                break;
            default:
                rmloli=true;
        }
    }
    if(rmloli) {
        lolion=0;
        while($('loli_frm')) {
            $('loli_frm').parentNode.removeChild($('loli_frm'));
        }
    }
    $('loliadd').className=cname;
    if($('loli_frm')) {
        $('loli_frm').contentWindow.postMessage(smode,'*');
    }
}

function updateTimeMg1(txt) {
    $('mg1_tim').innerHTML=txt;
}
var currentScene=0;
var backlayHide=false;
var backlayOpa=100;
var lastBacklayOpa=100;
var anaSceneOn=false;
function changeScene() {
    currentScene++;
    if(currentScene>4) {
        currentScene=0;
    }
    if(currentScene>3 && !anaSceneOn) {
        currentScene=0;
    }

    var frmSrc="stars3d-transparent.html?cache="+cache;
    $('mg1_tim').style.display=""; // none
    $('btn_dir').style.display="none";
    // missing a good forward 3d stars scene still x)
    $('btn_fot').className="lnkbtn";
    $('spaceship_sun').style.display=""; // none
    $('spaceship').style.maskImage="none";
    $('spaceship').style.webkitMaskImage="none";
    if(isChristmas) {
        $('christmastreeship_sun').style.display=""; // none
        $('christmastreeship').style.maskImage="none";
        $('christmastreeship').style.webkitMaskImage="none";
    }

    switch(currentScene) {
        case 1:
            frmSrc="stars-transparent.html?cache="+cache;
            //backlayHide=true;
            $('btn_dir').style.display=""; // inline-block
            break;
        case 2:
            firstMG1=0;
            frmSrc="../simulations/mg1/index.html?cache="+cache;
            //frmSrc="../mg1/index.html?cache="+cache;
            //backlayHide=true;
            $('mg1_tim').style.display="inline-block";
            $('spaceship_sun').style.display="block";
            if(isChristmas) {
                $('christmastreeship_sun').style.display="block";
            }
            if(!shipSunLoaded) {
                preloadSpaceshipSun();
                shipSunLoaded=true;
            }
            break;
        case 3:
            frmSrc="../void/void-2.html?cache="+cache;
            //backlayHide=true;
            if(lolion>0) {
                anaSceneOn=true;
                $('btn_fot').className="lnkbtn red";
            }
            break;
        case 4:
            frmSrc="//analiestar.com/immerse/index.php?cache="+cache;
            //backlayHide=true;
            $('btn_fot').className="lnkbtn blue";
            anaSceneOn=false;
            break;
        default:
            //backlayHide=false;
            $('btn_dir').style.display=""; // inline-block
    }

    $('star_frm').src=frmSrc;
    //alert("new scene : "+currentScene);
}

var fullScreen=false;
function toggle_full() {
    fullScreen=!fullScreen;
    //var elem = document.getElementById("myvideo");
    var elem = document.documentElement;

    if(fullScreen) {
        if (elem.requestFullscreen) {
            elem.requestFullscreen();
        } else if (elem.webkitRequestFullscreen) { /* Safari */
            elem.webkitRequestFullscreen();
        } else if (elem.msRequestFullscreen) { /* IE11 */
            elem.msRequestFullscreen();
        }
    } else {
        if (document.exitFullscreen) {
            document.exitFullscreen();
        } else if (document.webkitExitFullscreen) { /* Safari */
            document.webkitExitFullscreen();
        } else if (document.msExitFullscreen) { /* IE11 */
            document.msExitFullscreen();
        }
    }
}
//window.addEventListener('fullscreenchange', (e) => {
//    //fullScreen=!fullScreen;
//    console.log("fullscreen trigger "+fullScreen);
//});
</script>
</head>
<body onload="init();" onmousemove="mousehandle(event);" onmouseup="unlockHold();" ontouchmove="mousehandle(event);" ontouchend="unlockHold();">


<div id="backdrop">
<iframe src="stars3d-transparent.html?cache=36" frameborder="0" class="frm" id="star_frm"></iframe>
</div>

<div id="spaceship"></div>
<div id="spaceship_sun"></div>

<div id="backlay"></div>

<div id="lcd_cont">
<iframe src="ship-lcd.html?cache=36" frameborder="0" class="frm" id="lcd_frm"></iframe>
</div>
<div id="fo-cont" class="inverted-mask">
<div id="christmastreeship"></div>
<div id="christmastreeship_sun"></div>
<canvas id="flames-overlay"></canvas>
</div>

<!-- Audio element with controls and a sample audio file -->
<audio loop id="myAudio">
  <source src="1.mp3" type="audio/mpeg">
  Your browser does not support the audio element.
</audio>

<div id="above">
<div class="tbl" style="height: 100%;">
    <div class="tr" style="height: 50px;">
        <div class="td" style="text-align: center; vertical-align: middle;">
            <a href="javascript:toggle_full();" target="_self" id="btn_full" class="lnkbtn">
                <div id="title_swap" style="text-align: center; position: absolute; display: inline-block; left: 0; top: 8px; width: 100%;">
                    [ Fullscreen ]
                </div>
                <div id="title_c" style="display: inline-block; position: relative;  color: #666 !important;">
                    Twily AI Album #1
                </div>
            </a>
        </div>
    </div>
    <div class="tr">
        <div class="td">
        
        <div class="tbl">
            <div class="tr">
                <div class="td sidebar">
                    &nbsp;
                </div>
                <div class="td">

<center>
<div id="player_frm">
<div id="player">

<div class="tbl">
<div class="tr">
<div class="td" style="background: rgba(25,32,70,1.0); background: linear-gradient(0deg, #192046, #1e275c); /*#46192e, #172158);*/ border-radius: 2px;">


<!-- player -->
<div class="tbl" style="height: 100%;">
<div class="tr">
<div class="td" style="width: 150px; height: 100%; /* ff fix */">

<!-- player left -->
<div class="pad18" style="height: 100%;">
<div id="display">
<span id="triangle"></span>
<span id="time">00:00</span>
<!-- Canvas for visualization -->
<canvas id="visualizer"></canvas>
</div>
</div>

</div>
<div class="td">

<!-- player right -->

<div class="pad18">
<!--<div id="title"><marquee><div id="inner">1. </div></marquee></div>-->
<div id="title"><div id="inner">1. </div><input type="button" class="btn" value="-" onclick="minimize();" style="float: right;" /></div>
<br />
<div id="bar" onmousedown="lockseek();" ontouchstart="lockseek();">
<div id="progress"></div>
</div>
<br />
<div id="controls">
    <input type="button" class="btn" value="⊲" onclick="prev();" />
    <input type="button" class="btn" value="►" id="btnplaypause" onclick="playpause();" />
    <input type="button" class="btn" value="⊳" onclick="next();" />
    <input type="button" class="btn" value="≠" id="btnshuftog" onclick="shuftog();" /><label class="grey" id="lbshuf">Shuffle</label>
    <input type="button" class="btn" value="∞" id="btnlooptog" onclick="looptog();" /><label class="grey" id="lbloop">Loop one</label>
&nbsp;
    <input type="button" class="btn" value="⏏" onclick="dl();" title="Download" alt="Download" />
<div style="float: right;">
<input type="button" class="btn" value="~" id="btneqtog" onclick="eqtog();" />
<input type="button" class="btn" value="≣" id="btnlisttog" onclick="listtog();" />
<label id="lbvol">Vol:</label><div id="vol" onmousedown="lockvol();" ontouchstart="lockvol();">
<div id="level"></div>
</div>
</div>
<div style="clear:both;"></div>
</div>
</div>

</div>
</div>
</div>


</div>
</div>
<!-- separate equalizer start -->
<div class="tr">
<div class="td brd" id="eqbrd" style="background: rgba(25,32,70,1.0); background: linear-gradient(0deg, #1e275c, #192046); /*#46192e, #172158);*/ border-radius: 2px;">

<div class="pad18" id="eqfrm" style="display: none;">
<!--Equalizer
<br />-->

<center>
<div class="tbl">
<div class="tr">
<div class="td" style="vertical-align: top; text-align: left;">
<!-- left -->
<input type="button" class="btn" value="flat" onclick="loadpreset(0);" />
<br />
</div>
<div class="td">

<div class="tbl" style="width: 75%;"> <!-- db from +20 to 0 to -20 -->
<div class="tr">
<div class="td" style="width: 9%;">

<div class="tbl" style="height: 100%;text-align: right;">
<div class="tr">
<div class="td" style="vertical-align: top; padding-bottom: 6px;">
<label class="grey">+20</label>
</div>
</div>
<div class="tr">
<div class="td">
<label class="grey">-</label>
</div>
</div>
<div class="tr">
<div class="td" style="vertical-align: bottom; padding-top: 6px;">
<label class="grey">-20</label>
</div>
</div>
</div>

</div>
<div class="td" style="width: 9%;">
<div class="eqbar" onmousedown="lockeq(0);" ontouchstart="lockeq(0);">
<div class="eqlevel" id="eq0"></div>
</div>
</div>
<div class="td" style="width: 9%;">
<div class="eqbar" onmousedown="lockeq(1);" ontouchstart="lockeq(1);">
<div class="eqlevel" id="eq1"></div>
</div>
</div>
<div class="td" style="width: 9%;">
<div class="eqbar" onmousedown="lockeq(2);" ontouchstart="lockeq(2);">
<div class="eqlevel" id="eq2"></div>
</div>
</div>
<div class="td" style="width: 9%;">
<div class="eqbar" onmousedown="lockeq(3);" ontouchstart="lockeq(3);">
<div class="eqlevel" id="eq3"></div>
</div>
</div>
<div class="td" style="width: 9%;">
<div class="eqbar" onmousedown="lockeq(4);" ontouchstart="lockeq(4);">
<div class="eqlevel" id="eq4"></div>
</div>
</div>
<div class="td" style="width: 9%;">
<div class="eqbar" onmousedown="lockeq(5);" ontouchstart="lockeq(5);">
<div class="eqlevel" id="eq5"></div>
</div>
</div>
<div class="td" style="width: 9%;">
<div class="eqbar" onmousedown="lockeq(6);" ontouchstart="lockeq(6);">
<div class="eqlevel" id="eq6"></div>
</div>
</div>
<div class="td" style="width: 9%;">
<div class="eqbar" onmousedown="lockeq(7);" ontouchstart="lockeq(7);">
<div class="eqlevel" id="eq7"></div>
</div>
</div>
<div class="td" style="width: 9%;">
<div class="eqbar" onmousedown="lockeq(8);" ontouchstart="lockeq(8);">
<div class="eqlevel" id="eq8"></div>
</div>
</div>
<div class="td" style="width: 9%;">
<div class="eqbar" onmousedown="lockeq(9);" ontouchstart="lockeq(9);">
<div class="eqlevel" id="eq9"></div>
</div>
</div>
</div>
<div class="tr">
<div class="td center"><label class="grey">&nbsp;</label></div>
<div class="td center"><label class="grey">60</label></div>
<div class="td center"><label class="grey">170</label></div>
<div class="td center"><label class="grey">310</label></div>
<div class="td center"><label class="grey">600</label></div>
<div class="td center"><label class="grey">1K</label></div>
<div class="td center"><label class="grey">3K</label></div>
<div class="td center"><label class="grey">6K</label></div>
<div class="td center"><label class="grey">12K</label></div>
<div class="td center"><label class="grey">14K</label></div>
<div class="td center"><label class="grey">16K</label></div>
</div>
</div>

</div>
<div class="td" style="vertical-align: top; text-align: right;">
<!-- right -->
<input type="button" class="btn" value="preset1" onclick="loadpreset(1);" />
<br />
<input type="button" class="btn" value="preset2" onclick="loadpreset(2);" />
<br />
<input type="button" class="btn" value="preset3" onclick="loadpreset(3);" />
<br />
<input type="button" class="btn" value="preset4" onclick="loadpreset(4);" />
<br />
</div>
</div>
</div>
</center>

</div><!--pad18-->

</div>
</div>
<!-- separate playlist start -->
<div class="tr">
<div class="td brd" id="listbrd" style="background: rgba(16,20,38,.5);">

<!-- list -->

<div id="resizeFrm">
<div class="pad18" id="listfrm">

</div>
<div class="custom-resize-handle"></div>
</div>

</div>
</div>
</div>

</div> <!-- end player -->
</div>
</center>


                </div>
                <div class="td sidebar">
                    &nbsp;
                </div>
            </div>
        </div>
        
        </div>
    </div>
    <div class="tr" style="height: 50px;">
        <div class="td" style="text-align: center; vertical-align: middle;">
<div class="tbl">
    <div class="tr">
        <div class="td" style="width: 33%; text-align:left; padding-top: 28px;">
<a href="javascript:toggle_direction();" target="_self" id="btn_dir" class="lnkbtn">[ Change direction ]</a>
        <span id="mg1_tim"></span>
        </div>
        <div class="td" style="width: 34%;">
<div id="jswarning">This website requires JavaScript to function</div>
<br />
<a href="javascript:changeScene();" target="_self" class="lnkbtn" id="btn_fot">
<div class="tbl" style="position: absolute; width: 100%; height: 100%; margin-top: -8px; margin-left: -4px;"><div class="tr">
<div id="footer_swap" style="text-align: center; vertical-align: middle;" class="td">
[ Change Scene ]
</div>
</div></div>
<div id="footer_c" style="display: inline-block; color: #666 !important;">
Runtime: <span id="runtime"></span>
<br />
<br />
(produced with MusicGen~)
<br />
<br />
(ɔ) Twily 2025
</div>
</a>
<br />
<br />
        </div>
        <div class="td" style="width: 33%; text-align: right; padding-top: 28px;">
<a href="javascript:toggle_ship();" target="_self" id="btn_ship" class="lnkbtn">[ Loading spaceship ]</a>
        </div>
    </div>
</div>
        </div>
    </div>
</div>
</div>

<a id="displayMini_cont" href="javascript:maximize();" target="_self">
<div id="displayMini">
<span id="triangleMini"></span>
<span id="timeMini">00:00</span>
<!-- Canvas for visualization -->
<canvas id="visualizerMini"></canvas>
</div>
</a>
<div id="log"></div>
<a href="javascript:loliadd();" target="_self" id="loliadd"></a>

<script type="text/javascript">
$('jswarning').style.display="none";

var localtracks=[
    "1",90,
    "2",90,
    "3",90,
    "4",90,
    "5",90,
    "6",90,
    "7",90,
    "8",90,
    "9",90,
    "10",90,
    "11",80,
    "12",80,
    "13",80,
    "14",80,
    "15",80,
    "16",90,
    "17",90,
    "18",90,
    "19",90,
    "20",90,
    "21",90,
    "22",90,
    "23",90,
    "24",90,
    "25",90,
    "26",90,
    "27",90,
    "28",90,
    "29",90,
    "30",90,
    "31",90,
    "32",90,
    "33",90,
    "34",90
];
rearrangelist(1);

// grok visualizer implement

//Adapting for Volume
///To visualize volume instead, you can use time-domain data:
//
//Replace getByteFrequencyData(dataArray) with getFloatTimeDomainData(timeDataArray) (where timeDataArray is a Float32Array of length analyser.fftSize).
//
//
//
//Calculate the root mean square (RMS) to estimate volume:
//
//javascript
//
//
//const timeDataArray = new Float32Array(analyser.fftSize);
//analyser.getFloatTimeDomainData(timeDataArray);
//let sum = 0;
//for (let i = 0; i < timeDataArray.length; i++) {
//  sum += timeDataArray[i] * timeDataArray[i];
//}
//const rms = Math.sqrt(sum / timeDataArray.length); // Volume level
//
//
//Use rms to adjust the height of a single bar or scale a waveform.
//
//Notes on Pitch
//
//Extracting pitch directly is more complex and requires pitch
// detection algorithms (e.g., autocorrelation or finding the 
// dominant frequency from the frequency data). The frequency 
// spectrum provided by getByteFrequencyData gives raw frequency
// amplitudes, which can hint at pitch but don’t directly isolate 
// it for complex sounds. For a simple visualization, the spectrum i
// s often sufficient, as pitch relates to frequency content.

</script>


</body>
</html>

Top
©twily.info 2013 - 2025
twily at twily dot info



2 530 754 visits
... ^ v