~firefoxmp3
40 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
index.html
ship-lcd.html
spaceship.png
spaceship_sunlit.png
stars-transparent.html
stars3d-transparent.html


mp3index.html
65 KB• 102•  2 days ago•  DownloadRawClose
2 days ago•  102

{}
<!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: -215px;
    /*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;
    border-bottom-left-radius: 36px;
    background: transparent;
    /*box-shadow: 0 0 5px #621F33*/;
    opacity: 0.8;
    cursor: pointer;
    transition: .5s ease;
}
#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; }
.lolion.blue { background: rgba(124,105,255,.3) !important; }
.lolion.grey { background: rgba(97,97,97,.3) !important; }

#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;
}

.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";
        }
    } 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";
        }
    }
    if(lastShipOpa!=shipOpa) { 
        $('spaceship').style.opacity=(shipOpa/100);
        $('spaceship_sun').style.opacity=(shipOpa/100);
        $('lcd_cont').style.opacity=(shipOpa/100);
        lastShipOpa=shipOpa;
    }

    //console.log(mgLightVal+" "+mgLightValSet);
    if(!mgLightOn && mgLightVal>0 && mgLightValSet<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+"%)";
        //
        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(mgLightValSet>=1) {
            mgLightOn=true;
        }
        //console.log("Turning light on mlv: "+mlv+", mlvo: "+mlvo);
    } else if(mgLightOn && mgLightVal<1 && mgLightValSet>0) {
        mgLightValSet=Math.abs(mgLightVal);
        let mlv=((1-mgLightValSet)-.1)*(100+maxgrad);
        let mlvo=mlv+maxgrad;
        let transcolor="rgba(0,0,0,"+(mgLightValSet)+")";
        $('spaceship_sun').style.maskImage="linear-gradient(90deg, "+transcolor+" "+mlv+"%, black "+mlvo+"%)";
        $('spaceship_sun').style.webkitMaskImage="linear-gradient(90deg, "+transcolor+" "+mlv+"%, black "+mlvo+"%)";
        //
        let mlvi=((mgLightValSet)-.1)*(100+maxgrad);
        let mlvio=mlvi+maxgrad;
        transcolor="rgba(0,0,0,"+(1-mgLightValSet)+")";
        $('spaceship').style.maskImage="linear-gradient(-90deg, "+transcolor+" "+mlvi+"%, black "+mlvio+"%)";
        $('spaceship').style.webkitMaskImage="linear-gradient(-90deg, "+transcolor+" "+mlvi+"%, black "+mlvio+"%)";
        if(mgLightValSet<=0) {
            mgLightOn=false;
        }
        //console.log("Turning light off mlv: "+mlv+", mlvo: "+mlvo);
    } else if(mgLightOn && mgLightTun>mgLightTunSet && mgLightTun!=1) { // eclipse in
        mgLightTunSet=Math.abs(mgLightTun);
        let mlv=((1-mgLightTunSet)-.1)*(100+maxgrad);
        let mlvo=mlv+maxgrad;
        let transcolor="rgba(0,0,0,"+(1-(mgLightTunSet*.9))+")";
        $('spaceship_sun').style.maskImage="linear-gradient(90deg, black "+mlv+"%, "+transcolor+" "+mlvo+"%)";
        $('spaceship_sun').style.webkitMaskImage="linear-gradient(90deg, black "+mlv+"%, "+transcolor+" "+mlvo+"%)";
        //
        let mlvi=((mgLightTunSet)-.1)*(100+maxgrad);
        let mlvio=mlvi+maxgrad;
        transcolor="rgba(0,0,0,"+(mgLightTunSet)+")";
        $('spaceship').style.maskImage="linear-gradient(-90deg, black "+mlvi+"%, "+transcolor+" "+mlvio+"%)";
        $('spaceship').style.webkitMaskImage="linear-gradient(-90deg, black "+mlvi+"%, "+transcolor+" "+mlvio+"%)";
        //console.log("eclipse in "+mgLightTun);
    } else if(mgLightOn && mgLightTun<mgLightTunSet) { // eclipse out
        mgLightTunSet=Math.abs(mgLightTun);
        let mlv=((mgLightTunSet)-.1)*(100+maxgrad);
        let mlvo=mlv+maxgrad;
        let transcolor="rgba(0,0,0,"+(1-(mgLightTunSet*.9))+")";
        $('spaceship_sun').style.maskImage="linear-gradient(90deg, "+transcolor+" "+mlv+"%, black "+mlvo+"%)";
        $('spaceship_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,"+(mgLightTunSet)+")";
        $('spaceship').style.maskImage="linear-gradient(-90deg, black "+mlvi+"%, "+transcolor+" "+mlvio+"%)";
        $('spaceship').style.webkitMaskImage="linear-gradient(-90deg, black "+mlvi+"%, "+transcolor+" "+mlvio+"%)";
        //console.log("eclipse out "+mgLightTun);
    }

    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=24;
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;

    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;
}

function init() {
    $('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();

    newSize();
    keepAlive();

    loadvol();
    loadeq();
}

var shipOpa=0;
var lastShipOpa=0;
var shipOn=false;
var shipLoaded=false;
var shipSunLoaded=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.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";
    }
    img.src="./spaceship_sunlit.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); });
}

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

    var sca=biggest/960; // 1
    $('lcd_frm').style.transform="perspective(300px) rotateX(-3deg) scale("+sca+")";
    var invSca=1-sca;
    console.log("sca: "+sca);
    console.log("invSca: "+invSca);

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

    //$('lcd_frm').style.top=(biggest/2)+"px";
    $('lcd_frm').style.top=((biggest*.245)-(invSca*25))+"px";
    //$('lcd_frm').style.top=(biggest*.32)+"px";

    //var bcr=$('lcd_frm').getBoundingClientRect();
    //if(bcr.top<0) 

    // 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;
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;
                    //console.log(event.data[3]);
                    if(event.data[3]>=0 && event.data[3]<=Math.PI-lilen) {
                        //console.log("staying dark");
                        if(mgLightVal!=0) {
                            mgLightVal=0;
                        }
                    } else if(event.data[3]<=-0 && event.data[3]>=-lilen) {
                        //console.log("entering light");
                        mgLightVal=Math.abs(event.data[3]*limul);
                    } else if(event.data[3]<-lilen && event.data[3]>=-Math.PI) {
                        //console.log("staying light");
                        if(mgLightVal!=1) {
                            mgLightVal=1;
                        }
                        if(event.data[3]>-Math.PI/2-lilen && event.data[3]<-Math.PI/2+lilen) {
                            //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));
                        }
                        //console.log(mgLightTun); // 1 0 -1 or 1 0 1 or 0 1 0
                    } else if(event.data[3]>=Math.PI) {
                        //console.log("entering dark");
                        mgLightVal=((Math.PI-event.data[3])*limul);
                    }
                    //console.log("mgLightVal = "+mgLightVal);

                    if(firstMG1<2) {
                        let biggest=Math.max(window.innerWidth,window.innerHeight);
                        let sca=biggest/960; // 1
                        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"
                    } else {
                        $('loli_frm').style.pointerEvents="none"
                    }
                    break;
                case "loli_blk":
                    if(event.data[1]==1) {
                        $('loliadd').style.display="none";
                    } else {
                        $('loliadd').style.display="";
                    }
                    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) {
    if(lolion==0 && x!=0) {
        lolion=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);
        $('loliadd').className="lolion";
    } else if(lolion==1 && x!=0) {
        lolion=2;
        $('loliadd').className="lolion blue";
        $('loli_frm').contentWindow.postMessage(['slowmode',1],'*');
    } else if(lolion==2 && x!=0) {
        lolion=3;
        $('loliadd').className="lolion grey";
        $('loli_frm').contentWindow.postMessage(['slowmode',2],'*');
    } else {
        lolion=0;
        while($('loli_frm')) {
            $('loli_frm').parentNode.removeChild($('loli_frm'));
        }
        $('loliadd').className="";
    }
}

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";

    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;
            //backlayHide=true;
            $('mg1_tim').style.display="inline-block";
            $('spaceship_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=24" 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=24" frameborder="0" class="frm" id="lcd_frm"></iframe>
</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 490 639 visits
... ^ v