Categories
creative coding lab

Project B: Journey

link to my project:
https://calistalu.github.io/ProjectB/

link to my code:
https://github.com/calistalu/ProjectB

  • Part 1 for public readers
    Calista Lu,
    JOURNEY, 2023
    an immersive experience of rhythmic visuals
  • The Elevator Pitch.
    My project is committed to exploring a journey of self-discovery, taking the form of interactive audio visualization. The user is invited to immerse themselves in a non-narrative journey, experiencing an attractive blend of rhythmic visual elements.
  • Abstract.
    My project B is an abstract expression of the concept that everyone’s life experiences define their unique self, and invites users to think, engage, release and refresh, reflecting on their own journey of self-exploration. In this enchanting journey of audio-visual exploration, each step echoes with the heartbeat, and every scenery unfolds a symphony with the soul. It’s as if users are penning theirs unique existence on this vibrant canvas, striding forward like that solitary yet resolute silhouette. Hopefully users could be emotionally touched, purified and healed at the journey’s end.ready to start your journey 
  • Part 2 for yourself (and your instructor)
    1) Process: Design and Composition

    After deciding to use audio visualization to narrate my story, I drew inspiration for my visual elements from this video:
    I attempted to create a 3D music visualizer using WEBGL mode. However, I encountered a major challenge—the 3D canvas defaulted to a white background that covered everything. I turned to Professor Eric for help, who suggested me to use ‘CreateGraphics’ in p5, but that is only a way to combine P2D and WEBGL without resolving the overlapping issue. Despite trying various solutions, I ultimately abandoned the incorporation of my 3D music visualizer into the project.

    The website now consists of two webpages. The first serves as the initial interface and provides background information for users, while the second is the main music visualizer.

    3D music visualizer 2) Process: Technical

    I acquired advanced web development skills, including CSS animation and JS event listeners, from W3Schools with my intention of designing a visually appealing first webpage. Details on this aspect have been provided in my mini project7 documentation.

    One of the significant technical challenges was showcasing the growth process of a person in a way that cannot be reversed, because simply creating several buttons to choose the character is not what I really want, and ultimately, I used a Boolean for each stage to control the whole process.

    if (currentImage === "child") {
    if (start) {
    count1++;
    if (count1 < 150) {
    image(w1, map(count1, 0, 150, 0, width / 4), height - 100, 90, 120)
    stroke(255, 150)
    strokeWeight(10)
    line(0, height - 90, map(count1, 0, 150, 0, width / 4), height - 90)
    } else {
    count1 = 201
    stroke(255, 150)
    strokeWeight(10)
    image(w1, width / 4, height - 100, 90, 120)
    line(0, height - 90, width / 4, height - 90)
    }
    image(child, width / 2, height / 2 - 100 - 150, 150, 200);
    }
    textAlign(CENTER, CENTER);
    textSize(20);
    fill(255);
    strokeWeight(1)
    text(displayText, width / 2, height - 50);
    } else if (currentImage === "teen") {
    count2++;
    if (count2 < 150) {
    image(w2, map(count2, 0, 150, width / 4, width / 2), height - 100, 90, 120)
    stroke(255, 150)
    strokeWeight(10)
    line(0, height - 90, map(count2, 0, 150, width / 4, width / 2), height - 90)
    } else {
    count2 = 201
    stroke(255, 150)
    strokeWeight(10)
    image(w2, width / 2, height - 100, 90, 120)
    line(0, height - 90, width / 2, height - 90)
    }
    image(teen, width / 2, height / 2 - 100 - 150, 150, 200);
    } else if (currentImage === "adult") {
    count3++;
    if (count3 < 150) {
    image(w3, map(count3, 0, 150, width / 2, 3 * width / 4), height - 100, 90, 120)
    stroke(255, 150)
    strokeWeight(10)
    line(0, height - 90, map(count3, 0, 150, width / 2, 3 * width / 4), height - 90)
    } else {
    count3 = 201
    stroke(255, 150)
    strokeWeight(10)
    image(w3, 3 * width / 4, height - 100, 90, 120)
    line(0, height - 90, 3 * width / 4, height - 90)
    }
    image(adult, width / 2, height / 2 - 100 - 150, 150, 200);
    } else if (currentImage === "old") {
    count4++;
    if (count4 < 150) {
    image(w4, map(count4, 0, 150, 3 * width / 4, width), height - 100, 90, 120)
    stroke(255, 150)
    strokeWeight(10)
    line(0, height - 90, map(count4, 0, 150, 3 * width / 4, width), height - 90)
    } else {
    count4 = 201
    stroke(255, 150)
    strokeWeight(10)
    image(w4, width, height - 100, 90, 120)
    line(0, height - 90, width, height - 90)
    }
    image(old, width / 2, height / 2 - 100 - 150, 150, 200);
    } else if (currentImage === "end") {
    push()
    translate(width / 2, height / 2)
    image(end, 0, 0, width, height)
    pop()
    }

This part of code is still too long and I said I would shorten it in my presentation. So here is its optimized version:

//four stages + end
    function displayImage(imageIndex, imageRef, transitionCount, xPos, xEnd, y) {
        transitionCount++;
        if (transitionCount < 150) {
            const x = map(transitionCount, 0, 150, xPos, xEnd);
            image(imageRef, x, y - 100, 90, 120);
            stroke(255, 150);
            strokeWeight(10);
            line(0, y - 90, x, y - 90);
        } else {
            transitionCount = 201;
            stroke(255, 150);
            strokeWeight(10);
            image(imageRef, xEnd, y - 100, 90, 120);
            line(0, y - 90, xEnd, y - 90);
        }
        return transitionCount;
    }

    // Display images based on current stage
    imageMode(CENTER);
    if (currentImage === "child") {
        if (start) {
            count1 = displayImage("child", w1, count1, 0, width / 4, height);
            image(child, width / 2, height / 2 - 100 - 150, 150, 200);
        }
        textAlign(CENTER, CENTER);
        textSize(20);
        fill(255);
        strokeWeight(1)
        text(displayText, width / 2, height - 50);
    } else if (currentImage === "teen") {
        count2 = displayImage("teen", w2, count2, width / 4, width / 2, height);
        image(teen, width / 2, height / 2 - 100 - 150, 150, 200);
    } else if (currentImage === "adult") {
        count3 = displayImage("adult", w3, count3, width / 2, 3 * width / 4, height);
        image(adult, width / 2, height / 2 - 100 - 150, 150, 200);
    } else if (currentImage === "old") {
        count4 = displayImage("old", w4, count4, 3 * width / 4, width, height);
        image(old, width / 2, height / 2 - 100 - 150, 150, 200);
    } else if (currentImage === "end") {
        push();
        translate(width / 2, height / 2);
        image(end, 0, 0, width, height);
        pop();
    }

Another technical challenge involved making star particles sparkle more accurately in response to the song. The code triggers a blinking star when the amplitude level increases. I’ve experienced several versions and this one worked best.

//particles turn into star 
    let Amp = amplitude.getLevel();
    if (Amp - previousAmp > 0.05) {
        increasing = true
    } else {
        increasing = false
    }
    previousAmp = Amp;

    if (increasing) {
        randomIndex1 = floor(random(particles.length));
        randomIndex2 = floor(random(particles.length));
        randomIndex3 = floor(random(particles.length));
    }

    if (particles[randomIndex1]) {
        push()
        translate(width / 2, height / 2 + 100)
        translate(particles[randomIndex1].x, particles[randomIndex1].y)
        rotate(frameCount)
        image(star, 0, 0, 30, 30);
        pop()
    }

    if (particles[randomIndex2]) {
        push()
        translate(width / 2, height / 2 + 100)
        translate(particles[randomIndex2].x, particles[randomIndex2].y)
        rotate(frameCount)
        image(star, 0, 0, 40, 40);
        pop()
    }

    if (particles[randomIndex3]) {
        push()
        translate(width / 2, height / 2 + 100)
        translate(particles[randomIndex3].x, particles[randomIndex3].y)
        rotate(frameCount)
        image(star, 0, 0, 50, 50);
        pop()
    }

3) Reflection and Future Development

From the project proposal to its current version, many compromises were made due to time and technical limitations. While the CSS design of the first webpage was successful, I am less satisfied with how I represented my narrative in the second webpage. The initial intention was for it to be healing and inspiring.

Feedback from the audience suggested adding more differences in different life stages, a point I had also considered. Guest professors highlighted the importance of consistency between the two webpages in terms of visual style, including the design of circles and the choice of font.

What’s more, once moved to another device with a different screen ratio, some elements of my web page would be overlapping each other, which is a direction for future improvement.

4) Credits & References
https://www.bilibili.com/video/BV11N411w7Ys/?share_source=copy_web&vd_source=f652fb5afdc7d621b2d6acee94aa961c
[visually inspiration]

https://www.bilibili.com/video/BV1pr4y127Yz/?share_source=copy_web&vd_source=f652fb5afdc7d621b2d6acee94aa961c
[concept inspiration; image credit]

https://www.youtube.com/watch?v=akM4wMZIBWg&t=193s
[ Technical help: how to use the fft object to get waveform data and store them in an array; how to draw the wave into a circular shape by looping through the waveform data.]

 

Categories
creative coding lab

Mini Project 7: Project B Website Draft

  • Project Title
    Journey
  • Link to your project
    link to my project:
    https://calistalu.github.io/project-b-draft/
    link to my code:
    https://github.com/calistalu/project-b-draft
  • Brief Description and Concept
    My project B is committed to exploring a journey of self-discovery, taking the form of interactive audio visualization.  In my design, the whole project is composed of two web pages: the first serves as the initial interface as well as a background introduction for the users; and I plan to put my p5 sketch in the second website, inviting users to an immersive experience of rhythmic visuals.
  • Demo/Visual Documentation

  • Coding

    document.addEventListener('DOMContentLoaded', function() {
    const endOfPage=document.getElementById('box5');
    const contactBtn=document.getElementById('contactBtn');
    contactBtn.addEventListener('click', function() {
    endOfPage.scrollIntoView({ behavior:'smooth' });
    });
    });

    This portion of the code is responsible for generating a contact button that functions as a page redirect, taking the user to the bottom of the webpage upon being clicked. I sought resources like W3Schools to learn the utilization of event listeners in JavaScript to achieve this specific functionality.

    window.addEventListener("scroll", function() {
    const scrolled=window.scrollY;
    console.log(scrolled/10)
    const translationX=scrolled;
    const translationY=scrolled;
    document.getElementById("box2").style.transform="translate("+translationX+"px, "+translationY+"px) ";
    });

    Originally, my goal was to animate a brown pole’s rotation while having a circular element slide down it as users scrolled through the page. However, controlling these behaviors precisely proved challenging. Consequently, I opted to simplify the design, keeping the circle moving down to the right as the webpage is scrolled.

    \.frame1 {
    font-family: Cambria, Cochin, Georgia, Times, 'Times New Roman', serif;
    font-size: 30px;
    text-align: center;
    left: 25%;
    width: 50%;
    height: auto;
    padding: 20px;
    border-style: double;
    border-width: 6px;
    border-color: rgb(108, 95, 91);
    }
    
    .frame1 img {
    display: none;
    position: absolute;
    left: -300px;
    transform: translateY(-50%);
    width: 200px;
    height: 200px;
    }
    
    .frame1:hover img {
    display: block;
    }
    
    .frame1 img, .frame1 {
    transition: background-color 0.5s ease-out, transform 0.5s ease-out;
    }
    
    .frame1:hover{
    background-color: rgba(103, 114, 157,0.8);
    transform: rotate(5deg);
    }

    In the CSS section, I established interactive features for four distinct elements labeled “childhood, teenage, adulthood, old age.” The gif would appear when the mouse hovers over the specific bar, and disappear when mouse moves away through the use of pseudo classes, creating an engaging user experience. Initially, I assigned individual IDs to each bar, even though the interaction for the third bar is same as the first, as did the second and fourth bars. To enhance efficiency, I optimized the code by consolidating the shared interactions among bars into a single CSS class, retaining the IDs for defining their absolute positions. This approach reduced redundancy and greatly shortened the code.

  • Reflection
    1. Well-organized File Names make it easier to understand the purpose of each file, reducing confusion and the risk of misinterpreting file functionalities.2. I would use classes when multiple elements share similar style or behavior. They allow the application of the same styles to multiple elements without repeating code. Ids would be more commonly used, for in most cases an element has a distinct style or functionality that is specific on the page.

    3. I’m concerned about potential variations in the layout of my website across different devices since I haven’t utilized absolute positioning for all elements.”

    4. WYSIWYG Text Editor offers a visual, user-friendly interface for text editing and styling. It’s intuitive for those unfamiliar with coding, while HTML provides direct control over content structure and formatting.

    5. HTML/CSS defines the structure, content, and layout of web pages. JavaScript and p5.js enable interactive design, animations, and dynamic behaviors on web pages.