diff --git a/public_html/functions.js b/public_html/functions.js
index 0c98f90..6687b52 100644
--- a/public_html/functions.js
+++ b/public_html/functions.js
@@ -1,115 +1,135 @@
// gif player
document.querySelectorAll('.paused-animation').forEach(el => {
- el.addEventListener('click', () => {
- el.classList.toggle('active');
- });
+ el.addEventListener('click', () => {
+ el.classList.toggle('active');
+ });
});
// track players
const players = document.querySelectorAll('.track-player');
+
+function updateAllWaveformWidths() {
+ players.forEach(player => {
+ const container = player.querySelector('.track-container');
+ if (container) update_player_waveform_widths(container);
+ });
+}
+
+document.addEventListener('DOMContentLoaded', updateAllWaveformWidths);
+window.addEventListener('resize', updateAllWaveformWidths);
+
players.forEach(player => {
- const trackName = player.getAttribute('data-track');
+ const trackName = player.getAttribute('data-track');
const button = document.createElement('div');
button.className = 'play-button play'; // start as play
player.appendChild(button);
- const wrapper = document.createElement('div');
- wrapper.className = 'track';
- wrapper.innerHTML = `
+ const wrapper = document.createElement('div');
+ wrapper.className = 'track';
+ wrapper.innerHTML = `
`;
- player.appendChild(wrapper);
+ player.appendChild(wrapper);
- const playercontainer = player.querySelector('.track-container');
- const played = player.querySelector('.track-played');
- const unplayed = player.querySelector('.track-unplayed');
- played.style.backgroundImage = `url('tracks/${trackName}-played.png')`;
- unplayed.style.backgroundImage = `url('tracks/${trackName}.png')`;
+ const playercontainer = player.querySelector('.track-container');
+ const played = player.querySelector('.track-played');
+ const unplayed = player.querySelector('.track-unplayed');
+ played.style.backgroundImage = `url('tracks/${trackName}-played.png')`;
+ unplayed.style.backgroundImage = `url('tracks/${trackName}.png')`;
- const audio = document.createElement('audio');
- audio.style.display = 'none';
- player.appendChild(audio);
+ const audio = document.createElement('audio');
+ audio.style.display = 'none';
+ player.appendChild(audio);
- button.addEventListener('click', () => {
- // Pause other players immediately
- players.forEach(p => {
- const otherAudio = p.querySelector('audio');
- const otherBtn = p.querySelector('.play-button');
- if (otherAudio !== audio) {
- if (!otherAudio.paused) otherAudio.pause();
- if (otherBtn) {
- otherBtn.classList.add('play');
- otherBtn.classList.remove('pause');
- }
- }
- });
+ button.addEventListener('click', () => {
+ // Pause other players immediately
+ players.forEach(p => {
+ const otherAudio = p.querySelector('audio');
+ const otherBtn = p.querySelector('.play-button');
+ if (otherAudio !== audio) {
+ if (!otherAudio.paused) otherAudio.pause();
+ if (otherBtn) {
+ otherBtn.classList.add('play');
+ otherBtn.classList.remove('pause');
+ }
+ }
+ });
- // Then lazy-load and play/pause current audio as before...
- if (!audio.src) {
- audio.src = 'tracks/' + trackName + '.mp3';
- audio.currentTime = 0;
- audio.load();
+ // Then lazy-load and play/pause current audio as before...
+ if (!audio.src) {
+ audio.src = 'tracks/' + trackName + '.mp3';
+ audio.currentTime = 0;
+ audio.load();
- audio.addEventListener('canplay', () => {
- audio.play().then(() => {
- button.classList.remove('play');
- button.classList.add('pause');
- }).catch(() => {
- button.classList.add('play');
- button.classList.remove('pause');
- });
- }, { once: true });
+ audio.addEventListener('canplay', () => {
+ audio.play().then(() => {
+ button.classList.remove('play');
+ button.classList.add('pause');
+ }).catch(() => {
+ button.classList.add('play');
+ button.classList.remove('pause');
+ });
+ }, { once: true });
- return;
- }
+ return;
+ }
- if (audio.paused) {
- audio.play().then(() => {
- button.classList.remove('play');
- button.classList.add('pause');
- }).catch(() => {
- button.classList.add('play');
- button.classList.remove('pause');
- });
- } else {
- audio.pause();
- button.classList.add('play');
- button.classList.remove('pause');
- }
- });
+ if (audio.paused) {
+ audio.play().then(() => {
+ button.classList.remove('play');
+ button.classList.add('pause');
+ }).catch(() => {
+ button.classList.add('play');
+ button.classList.remove('pause');
+ });
+ } else {
+ audio.pause();
+ button.classList.add('play');
+ button.classList.remove('pause');
+ }
+ });
- audio.addEventListener('ended', () => {
- button.classList.add('play');
- button.classList.remove('pause');
- });
+ audio.addEventListener('ended', () => {
+ button.classList.add('play');
+ button.classList.remove('pause');
+ });
- playercontainer.addEventListener('click', e => {
- const rect = playercontainer.getBoundingClientRect(); // Use container rect!
- const clickX = e.clientX - rect.left;
- const width = rect.width;
+ playercontainer.addEventListener('click', e => {
+ const rect = playercontainer.getBoundingClientRect(); // Use container rect!
+ const clickX = e.clientX - rect.left;
+ const width = rect.width;
- const portion = Math.min(Math.max(clickX / width, 0), 1);
+ const portion = Math.min(Math.max(clickX / width, 0), 1);
- audio.currentTime = portion * audio.duration;
- });
+ audio.currentTime = portion * audio.duration;
+ });
- audio.addEventListener('timeupdate', () => {
- const portion = audio.duration ? audio.currentTime / audio.duration : 0;
- update_play_position(played, unplayed, portion);
- });
+ audio.addEventListener('timeupdate', () => {
+ const portion = audio.duration ? audio.currentTime / audio.duration : 0;
+ update_play_position(played, unplayed, portion);
+ });
+ update_play_position(played, unplayed, 0.03);
});
function update_play_position(played_element, unplayed_element, portion) {
- const playedPercent = portion * 100;
- const unplayedPercent = 100 - playedPercent;
- played_element.style.width = playedPercent + "%";
- unplayed_element.style.width = unplayedPercent + "%";
+ const playedPercent = portion * 100;
+ const unplayedPercent = 100 - playedPercent;
+ played_element.style.width = playedPercent + "%";
+ unplayed_element.style.width = unplayedPercent + "%";
+}
+
+function update_player_waveform_widths(player_container_element) {
+ const trackWidth = player_container_element.offsetWidth;
+ const played_element = player_container_element.querySelector('.track-played');
+ const unplayed_element = player_container_element.querySelector('.track-unplayed');
+ played_element.style.backgroundSize = trackWidth + "px 80px";
+ unplayed_element.style.backgroundSize = trackWidth + "px 80px";
}
@@ -158,23 +178,53 @@ window.onload = function() {
// Create vertices
const vertices = [
- [-1, t, 0], [1, t, 0], [-1, -t, 0], [1, -t, 0],
- [0, -1, t], [0, 1, t], [0, -1, -t], [0, 1, -t],
- [t, 0, -1], [t, 0, 1], [-t, 0, -1], [-t, 0, 1]
+ [-1, t, 0],
+ [1, t, 0],
+ [-1, -t, 0],
+ [1, -t, 0],
+ [0, -1, t],
+ [0, 1, t],
+ [0, -1, -t],
+ [0, 1, -t],
+ [t, 0, -1],
+ [t, 0, 1],
+ [-t, 0, -1],
+ [-t, 0, 1]
].map(v => new THREE.Vector3(v[0] * normRadius, v[1] * normRadius, v[2] * normRadius));
// Define edges (pairs of vertex indices)
const edges = [
- [0, 11], [0, 5], [0, 1], [0, 7], [0, 10],
- [1, 5], [1, 7], [1, 8], [1, 9],
- [2, 3], [2, 4], [2, 6], [2, 10], [2, 11],
- [3, 4], [3, 6], [3, 8], [3, 9],
- [4, 5], [4, 9], [4, 11],
- [5, 9], [5, 11],
- [6, 7], [6, 8], [6, 10],
- [7, 8], [7, 10],
+ [0, 11],
+ [0, 5],
+ [0, 1],
+ [0, 7],
+ [0, 10],
+ [1, 5],
+ [1, 7],
+ [1, 8],
+ [1, 9],
+ [2, 3],
+ [2, 4],
+ [2, 6],
+ [2, 10],
+ [2, 11],
+ [3, 4],
+ [3, 6],
+ [3, 8],
+ [3, 9],
+ [4, 5],
+ [4, 9],
+ [4, 11],
+ [5, 9],
+ [5, 11],
+ [6, 7],
+ [6, 8],
+ [6, 10],
+ [7, 8],
+ [7, 10],
[8, 9], /*[8, 10],*/
- /*[9, 11],*/ [10, 11]
+ /*[9, 11],*/
+ [10, 11]
];
return {
@@ -224,7 +274,7 @@ window.onload = function() {
// 1. A thin cylindrical tube with back material that ignores depth
// 2. A cylindrical tube with front material that uses depth testing
const backRadius = 0.025; // thin for back lines
- const frontRadius = 0.035; // thick for front lines
+ const frontRadius = 0.035; // thick for front lines
icosa.edges.forEach((edge, index) => {
const start = icosa.vertices[edge[0]];
@@ -261,10 +311,26 @@ window.onload = function() {
// Define faces of icosahedron (each is a triangle of vertex indices)
const faces = [
- [0, 11, 5], [0, 5, 1], [0, 1, 7], [0, 7, 10], [0, 10, 11],
- [1, 5, 9], [5, 11, 4], [11, 10, 2], [10, 7, 6], [7, 1, 8],
- [3, 9, 4], [3, 4, 2], [3, 2, 6], [3, 6, 8], [3, 8, 9],
- [4, 9, 5], [2, 4, 11], [6, 2, 10], [8, 6, 7], [9, 8, 1]
+ [0, 11, 5],
+ [0, 5, 1],
+ [0, 1, 7],
+ [0, 7, 10],
+ [0, 10, 11],
+ [1, 5, 9],
+ [5, 11, 4],
+ [11, 10, 2],
+ [10, 7, 6],
+ [7, 1, 8],
+ [3, 9, 4],
+ [3, 4, 2],
+ [3, 2, 6],
+ [3, 6, 8],
+ [3, 8, 9],
+ [4, 9, 5],
+ [2, 4, 11],
+ [6, 2, 10],
+ [8, 6, 7],
+ [9, 8, 1]
];
// Create invisible faces for depth testing
@@ -279,7 +345,7 @@ window.onload = function() {
geometry.setAttribute('position', new THREE.BufferAttribute(positions, 3));
const faceMesh = new THREE.Mesh(geometry, faceMaterial);
- faceMesh.scale.set(0.975,0.975,0.975);
+ faceMesh.scale.set(0.975, 0.975, 0.975);
faceMesh.renderOrder = 0; // Render first for depth buffer
group.add(faceMesh);
@@ -288,19 +354,19 @@ window.onload = function() {
const internalMesh3 = new THREE.Mesh(geometry, internalMaterial);
const internalMesh4 = new THREE.Mesh(geometry, internalMaterial);
const internalMesh5 = new THREE.Mesh(geometry, internalMaterial);
- internalMesh1.scale.set(0.85,0.85,0.85);
+ internalMesh1.scale.set(0.85, 0.85, 0.85);
internalMesh1.renderOrder = 1; // Render between front and back
group.add(internalMesh1);
- internalMesh2.scale.set(0.65,0.65,0.65);
+ internalMesh2.scale.set(0.65, 0.65, 0.65);
internalMesh2.renderOrder = 1; // Render between front and back
group.add(internalMesh2);
- internalMesh3.scale.set(0.5,0.5,0.5);
+ internalMesh3.scale.set(0.5, 0.5, 0.5);
internalMesh3.renderOrder = 1; // Render between front and back
group.add(internalMesh3);
- internalMesh4.scale.set(0.4,0.4,0.4);
+ internalMesh4.scale.set(0.4, 0.4, 0.4);
internalMesh4.renderOrder = 1; // Render between front and back
group.add(internalMesh4);
- internalMesh5.scale.set(0.3,0.3,0.3);
+ internalMesh5.scale.set(0.3, 0.3, 0.3);
internalMesh5.renderOrder = 1; // Render between front and back
group.add(internalMesh5);
});
@@ -315,7 +381,7 @@ window.onload = function() {
const length = direction.length();
// Create cylinder
- const geometry = new THREE.CylinderGeometry(radius, radius, length*lengthmultiplier, 4, 1);
+ const geometry = new THREE.CylinderGeometry(radius, radius, length * lengthmultiplier, 4, 1);
// By default, cylinder is along Y-axis, so rotate it
geometry.rotateX(Math.PI / 2);
@@ -340,26 +406,26 @@ window.onload = function() {
let lastFrameTime = 0;
const targetFPS = 20;
- const animMultiplier = 30/targetFPS;
+ const animMultiplier = 30 / targetFPS;
const frameDuration = 1000 / targetFPS;
// Animation function
function animate(now) {
- requestAnimationFrame(animate);
+ requestAnimationFrame(animate);
- const delta = now - lastFrameTime;
- if (delta < frameDuration) return;
+ const delta = now - lastFrameTime;
+ if (delta < frameDuration) return;
- lastFrameTime = now;
+ lastFrameTime = now;
- // Rotate if animation is enabled
- if (animating) {
- icosahedronGroup.rotation.x -= 0.002 * animMultiplier;
- icosahedronGroup.rotation.y += 0.004 * animMultiplier;
- icosahedronGroup.rotation.z -= 0.001 * animMultiplier;
- }
+ // Rotate if animation is enabled
+ if (animating) {
+ icosahedronGroup.rotation.x -= 0.002 * animMultiplier;
+ icosahedronGroup.rotation.y += 0.004 * animMultiplier;
+ icosahedronGroup.rotation.z -= 0.001 * animMultiplier;
+ }
- renderer.render(scene, camera);
+ renderer.render(scene, camera);
}
// Start animation
diff --git a/public_html/index.html b/public_html/index.html
index 9e8e084..b1b9e0c 100644
--- a/public_html/index.html
+++ b/public_html/index.html
@@ -12,8 +12,8 @@
-
-
+
+
@@ -50,28 +50,14 @@
music releases
-
+
-
+
lastfuture signal to noise
listen or buy on bandcamp
@@ -80,14 +66,7 @@
-
+
lastfuture dreamshifter
listen or buy on bandcamp
@@ -95,14 +74,7 @@
-
+
lastfuture misinterpreter
listen or buy on bandcamp
@@ -111,29 +83,15 @@
-
+
lastfuture reverse kill
listen or buy on bandcamp
- highlighted tracks
-
+ highlight tracks
+
@@ -153,14 +111,7 @@
from the lab
-
+
@@ -187,4 +138,5 @@