You can interact with Luxuria Here:
https://suqichen777.github.io/CCLab/ProjectA/
Please keep in mind that Luxuria is very dangerous!
Here is a detailed blog post about my design on Luxuria. If you are only interested in the final showing condition, you can go to the simplified blog or Github Page. The following content in this blog post will include my Design Process, Coding Process, Reflection & Future Development, and Credits & Reference.
My design process got started with Mini Project 3 and Project Proposal, and the initial visual design is already fully elaborated in the two blog posts. I will go on with my further design in this blog post.
On the left, it presents where I get started with Luxuria:
Only a simple, stylized line, creating "a blocky, step-like pattern" like "the minimalist silhouette of a city skyline, with buildings of varying heights" (This comment was generated by Gemini). The very first step of my design is to make Luxuria more like "city night," and LLM (Gemini and ChatGPT) helped with my brainstorm. They have generated many solutions, including adding windows, creating "a wet ground" to reflect the buildings, adding cars going left and right, creating "depth" between different buildings, and changing the background color. Thus, I first combined my background created in Mini Project 4 with Luxuria and reconstructed my code. A more detailed description can be found in Mini Project 5.
Then I made a simple change to my cursor design. The original cursor is the default computer cursor, and I felt that it might not fit the overall storytelling, and a little bit embarrassing on the canvas. I took part in my storytelling about the "inner concept" of Luxuria: it was generated by the desire, as desire is also the source of everything in the world. What should be the beginning of human beings? I think it is "fire"; the denotation of "human beings" might also be "fire". Therefore, I made the cursor become a "sparkling cursor" which might remind the audience of fire, as shown below.
It might not fit the overall storytelling, and a little bit embarrassing on the canvas.
It should be noted that also in the .gif image, you can find my original cursor. But it is because the default video recording provided by macOS will capture the cursor itself. It disappears if you try to play with Luxuria yourself. I have tried to implement another way of dynamic cursor: I would like to implement plenty of floating circles in the background, and they will be affected by the cursor movement like "Repulse Effect". You can take the video below as a reference:
However, due to the difficulty of recording the position and movement of each circle without an array, I eventually gave the idea up and applied the simple sparkling version.
Another distinguishing design I made was to extend the whole canvas to make it twice 1600 * 500 as long as the logic canvas (800 * 500).
I made this decision because I don't want everything created by Luxuria to disappear right after reaching the right edge. In other words, this modification is made to make sure the storytelling that "Luxuria can make what you think reality" is interpreted in my final design. On the left is "pre-canvas", and on the right is my "current-canvas"; the right one would be moved to the left once completed to make sure it will be connected to the next continued line. More details about how I implement it and the inner logic will be explained in the coding process.
Finally, I turned to the brainstorming offered by generative AI above. I have tried several changes. One of them was implementing "cars" going back and forth, but I found it did not fit with my "buildings" since there was not enough space at the bottom to make sure "cars" would be "cars on the ground", and sometimes they conflicted with the "building" itself. Considering my background as a night sky with "breath of the universe", it is really weird to have cars in the blue background. What I really applied to my final version includes:
Windows: My very first thought about having windows was like "when I building get created, create a window". However, it was challenged when implementing the code, since a building is created by clicking. However, there might be several types of buildings:
In other words, it might be easy for me to discover if something is a building or not, while the computer can only see the difference between different clicks, while the shape of a building can be designed by the number of clicks, width, and height. And the position of the generated "one window" might also be strange, since a window appeared at the top of the "very irregular building" during my former coding. I had to give up this design and select another plan: using small windows generated by Luxuria, instead of clicking.
As shown on the right, it actually works better than expected since I made it "random" light windows, which makes the buildings look like real buildings at night at a glance. The way I generated the windows will also be explained in the coding part.
Building "Depth": I took the reference from an AI-generated Night City on StockCake and found that, in fact, many designs of "night city" do not have only a single layer, but multiple buildings standing at different distances from the screen. It seems that another z-axis going from our eyesight, which is vertical to the screen itself, can be placed in my canvas. I used random transparency of different blocks, changed as I clicked to create a sense of depth.
In fact, I am not satisfied with the final version, and I think the transition between different buildings is still too stiff to some degree. I think a better version can be created with smoother mathematical computation, along with some noise effect and flow control.
I have made some small adjustments at the end of my design process, including adding the frequency of light windows' appearance as the user clicks, and making the background darker while Luxuria itself brighter. One change I made but did not accept was to change the background from black-blue to red-pink-orange-yellow (like sunset). The reason for not applying this change was that, first, I did not want to discard my current design of waves in the background, and second, it looked too similar to my former design on the right (for the Game Design course in New York) to make me "proud" of an "innovative design". My reflection on my design will be stated later.
While the definition of my self-defined functions are available in Mini Project 5, my final code is organized in the structure assisted by them:
Before Setup()
create all variables, including 4 Layers and all other variables
Layers: previousFrameLayer, bgLayer, movingLayer, lineLayer; only movingLayer is cleared in each frame.
Setup()
initialize 4 layers with width 800 and height 500
initialize all other variables
initialize the background once
bgMetadataInit()
createBgColorAndWave()
create3CCSets
Draw()
place previousFrameLayer at (0,0)
place bgLayer at (800,0)
place lineLayer at (800,0) after drawing lines and windows
place movingLayer at (800,0) after drawing the sparkling cursor
update the line data
if the line is reaching the right edge (if (curX > trueWidth), curX is the line's ending X, while the trueWidth = 800, both of the two definitions are relative to the lineLayer canvas instead of the whole canvas with the width of 1600):
update previousFrameLayer
restart line x position to 0 (of line layer)
update line color
re-initialize a background inside bgLayer with
bgMetadataInit()
createBgColorAndWave()
create3CCSets
make windows brighter
clear line layer
Then I will explain my code for the different layers. Since what has been done in the bgLayer has already been explained in the code above (please turn to Mini Project 5 for reference), the following part will include the previous-frame layer, the line-layer, and the moving-layer.
As stated above, the switch between two logical canvas is designed with the help of a so-called previousFrameLayer. Here is how it works:
First, this layer is initialized along with the "true canvas":
While the background, line, and moving layer are placed on the right canvas, the previousFrameLayer is placed on the left inside the draw function.
The previousFrameLayer is updated with the code below when Luxuria reaches the right edge of the "true canvas". It should be noted that many other restarters are also placed in this if condition, such as clearing the lineLayer to implement the next movement of Luxuria. Therefore, the previousFrameLayer updates that copy the content of the current two static layers are always placed at the beginning of if (curX > trueWidth).
The moving-layer is the reason for my use of multiple layers. I need to keep the Luxuria line drawn in the previous frame visible, leading to the limitation of background() update in draw(). However, since I would like to have a sparkling cursor without afterimages drawn in the frames before, I still need to clear the overall canvas. Therefore, for each frame, I choose to use background() and clear the movingLayer while not clearing but redrawing the lineLayer. Nothing else dynamic was implemented due to the time limit; thus, what was done in the moving-layer includes only the sparkling cursor.
The moving layer is cleared in each frame, and the position of the current mouse is recorded. I would like to create a sense of "Desire will never stop" and choose to use a controlledX in the following implementation. In other words, the mouse could move around, but the x position of the sparkling cursor would only appear near the current end of the Luxuria line: controlledX = constrain(localMouseX, curX, curX + 50);
I also generate 12 small circles to make the circle sparkle with a random angle, a random distance towards the centre, a random position, a random transparency, and a random size for each. Finally, the centre circle, which represents the "true cursor" under a controlled X position, is drawn with the same color as Luxuria with a certain size.
The line layer is actually Luxuria movement.
The code shown on the left is all the elements drawn in the line layer, including the line color, strokeWeight (line width), and line position. The buildings are created as black rectangles with random transparency, while the windows are drawn as the line moves. The "next" y position of the line, the building transparency, and the frequency of the windows are all decided or influenced by mouse clicking.
The windows are drawn with a drawWindow() function, which takes windowWidth, windowHeight, ySpacing for each window, and windowColor as parameters. Although my current version determines these parameters as constant data, it could be improved with dynamic changes in the draw() function in the future.
Inside the drawWindow() function, it draws windows onto the lineLayer. It loops vertically down the building's up edge, and at every ySpacing interval, it takes a certain frequency (windowPossibility) to randomly decide whether to draw a window. If it does, it uses the windowColor, which can be updated each time the line reaches the right edge to draw a small rectangle at a random position to the left of preX (inside the building).
Other coding details of the line including adding 1 to its end and updating current x and y position to the previous position to make sure it is always moving forward. The line's color is updated regularly as a sin wave each time reaching the right edge.
Compared to the very beginning of my first project proposal, my final version of Project A elaborated the storytelling to make it more complete. I also make many efforts to make the default "nightcityscape" more realistic instead of the simple shape constructed by a single line. I think my development of building appearance with different transparency and random window light satisfies my own expectations on what the final night city should look like. However, there is still something I have not fully explored yet.
As Gohai suggested, Luxuria could be more complex with a more random design on the night cityscape. It should have more types of roofs, windows, and interior decoration. Another suggestion I got during presentation day was that the bacterium itself does not have too many interactive elements. Therefore, I might improve the complexity of Luxuria using more self-designed shapes and decorations using the vertex and translate() functions.
One feedback I received from our guest critics was that, although my design of storytelling, bacteria movement, and canvas design work well with each other, the background could not match the overall scene well. It might be hard to connect the waves and concentric circles to the motion of Luxuria during interaction.
This is also what confused me during my design since I could not find a way to make it blend into the overall concept. I have to admit that I was too proud of my findings during Mini Project 4 to give up what I had obtained during the initial background design.
I have thought about making Luxuria interact with the elements, like when it passes too close to a black hole's edge, they are captured by its gravity and spirals in towards the center. However, due to the difficulty in adjusting the static line layer, I did not adopt this idea.
Looking back, I should have discarded the original background design since it wasn't a good fit. I shouldn't have let sunk costs compromise the project's success and should have redesigned it to be more consistent with the overall concept.
Finally, I feel unsatisfied with some extendable elements of my storytelling.
Luxuria looks like Night City "by default," but it could actually become everything as the interaction makes it possible. I have only made the default version available, but the dynamically changing part was left totally unexplored due to the time limit 😭. In fact, what appeared in the storytelling part, especially "when you acknowledge its existence, it becomes reality since you are now only a victim," was a compensation for unfinished visual design.
Luxuria can also make some impact and allure human beings with eye contact (although it does not have a pair of eyes). I anticipate using some other interaction with the camera and AI-detactable eyesight in the future.
Official P5.JS Reference, especially for the use of different layers
Gohai (our dear professor)
Everyone who gave me feedback on Luxuria (at the risk of its danger)
Some design of Luxuria is inspired by 2 Situation Puzzle videos on Bilibili (in Chinese):
Visial Inspiration: AI generated "pixel night city" on StockCake