Artist Website

Hero Text on Music Artist Website I developed. Effect achieved using layers of Text Shadow in CSS.

Hero Text on Music Artist Website I developed. Effect achieved using layers of Text Shadow in CSS.

Artist Website

Project Brief

I designed and developed a website for the hip hop artist 1030 Tuwop whom one of my friends used to manage. The only instructions I was given were to create a website showcasing the artists music and personal style. My goal going into creating this website was to showcase interesting animations throughout the page as well as include areas of valuable interaction for users visiting the site to get a better understanding of the artist himself.

Project Scope

Client: 1030 Tuwop

Role: UX/UI Designer, Project Manager, Front-End Developer

Project type: Musician Website

Platform: Responsive Website

Project Length: 2 months

Deliverables: Finished Website

Tools: Adobe XD, HTML5, CSS3, SASS, Javascript, CSS Grid, CSS Animations, jQuery

Home Page

I chose to display Tuwop’s six most recent/most popular videos in a manner that was interactive for users and would display his colorful charisma and personality. I used CSS Grid to achieve the basic layout of the thumbnails which could be clicked on to be redirected to YouTube videos of his. The CSS for the grid items was as follows:

.grid {
  margin: 50vh auto 0;
  position: relative;
  width: 100%;
  display: grid;
  grid-template-columns: repeat(2, 1fr);

.item {
  margin: 0 0 30vh;
  position: relative;

.item:nth-child(odd) {
  margin-top: -30vh;

I ended up adding the .item:nth-child(odd) margin-top styling because I thought this grid layout with each item sort of overlapping one another across from each other was more interesting and the best use of space as opposed to a normal 2 column grid. Below is the code for the individual thumbnail styling and the hover/glitch effects:

 .item-title {
  position: absolute;
  text-shadow: 0 1px 0 #4f0099, 0 2px 0 #4c0094, 0 3px 0 #4a008f, 
  0 4px 0 #450085, 0 5px 0 #3f007a, 0 6px 0 #3a0070, 
  0 7px 0 #350066, 0 8px 0 #2f005c, 0 9px 0 #28004d, 
  0 0 5px rgba(0, 0, 0, 0.05), 0 1px 3px rgba(0, 0, 0, 0.2), 
  0 3px 5px rgba(0, 0, 0, 0.2), 0 5px 10px rgba(0, 0, 0, 0.2), 
  0 10px 10px rgba(0, 0, 0, 0.2), 0 20px 20px rgba(0, 0, 0, 0.3);
  opacity: 0;
  margin: 0;
  text-transform: lowercase;
  font-size: 6vw;
  color: #5600a8;
  font-family: "futura", sans-serif;
  font-weight: 800;
  padding: 30vh 0.5em 0;
  top: 0;
  pointer-events: none;
  line-height: 1;

.item:nth-child(odd) .item-title {
  right: 0;
  text-align: right;
  padding-top: 10vh;

.item-title span {
  display: block;
  position: relative;
  padding-top: 30px;
  font-size: 20%;
  font-weight: 500;
  opacity: 0;
  color: white;
  text-shadow: none;
  text-transform: uppercase;
  letter-spacing: 5px;

.glitch:hover + .item-title span {
  opacity: 1;
  animation: span-anim 0.5s linear;

.item:nth-child(odd):hover .item-title {
  opacity: 1;
  animation: title-2 0.5s linear;

.item:nth-child(even):hover .item-title {
  opacity: 1;
  animation: title 0.5s linear;

.glitch {
  position: relative;
  width: 40vmax;
  max-width: 400px;
  height: calc(40vmax * 1.25);
  max-height: calc(400px * 1.25);
  overflow: hidden;
  margin: 0 auto;

.glitch:hover {
  cursor: pointer;

.img {
  position: absolute;
  width: 400px;
  height: 400px;
  background: url("../img/img8.jpg") no-repeat 50% 0;
  background-color: transparent;
  background-size: cover;
  transform: translate3d(0, 0, 0);
  background-blend-mode: none;

.over1 .img {
  background-image: url("../img/img6.jpg");

.over2 .img {
  background-image: url("../img/img5.jpg");

.over3 .img {
  background-image: url("../img/img3.jpg");

.over4 .img {
  background-image: url("../img/img1.jpg");

.over5 .img {
  background-image: url("../img/img9.jpg");

.img:nth-child(n+2) {
  opacity: 0;

.glitch:hover .img:nth-child(n+2) {
  opacity: 1;

.glitch:hover .img:nth-child(2) {
  transform: translate3d(20px, 0, 0);
  animation: anim1 1.8s infinite linear alternate;

.glitch:hover .img:nth-child(3) {
  transform: translate3d(calc(-1 * 20px), 0, 0);
  animation: anim2 1.8s infinite linear alternate;

.glitch:hover .img:nth-child(4) {
  transform: translate3d(0, calc(-1 * 2px), 0) scale3d(-1, -1, 1);
  animation: anim3 1.8s infinite linear alternate;

.glitch:hover > .img:nth-child(5) {
  animation: flash 0.5s steps(1, end) infinite;

I used the same text-shadow effect on the thumbnail title text as I did the main hero-text on the home page for consistency purposes and cohesion. Text-align: right on the odd nth-child’s was important for visual spacing as well. When hovered upon, the thumbnail text would appear from the middle of the page and translate in either direction towards its respective thumbnail which drew the eye to the individual thumbnails themselves and not anywhere else on the page. The glitch effect and the text-effect for the thumbnails consisted of multiple layers of 3d-translate and clip-path calculations. Essentially multiple layers of the thumbnail image were laid upon each other and manipulated in different ways to achieve the effect. You can check out the rest of the proj.css file for the clip-path thumbnail image animations on my github if you would like to see more of the code!

Another aesthetic component I included was the page background color change when hovering over each individual thumbnail. I thought this would provide users another interaction point that would leave a positive impression on them. I essentially created a simple JS script on the homepage that would change the background color to the color closest to that of the individual thumbnail when hovered upon like below:

document.querySelector(".over").onclick = function() {"", "_blank");

  document.querySelector(".over").onmouseenter = function() { = "#e0bf02";
    document.querySelector(".header").style.backgroundColor = "#e0bf02";

  document.querySelector(".over").onmouseleave = function() { = "black";
    document.querySelector(".header").style.backgroundColor = "black";

    document.querySelector(".over1").onclick = function() {"", "_blank");

  document.querySelector(".over1").onmouseenter = function() { = "#601968";
     document.querySelector(".header").style.backgroundColor = "#601968";

Studio Page

I created the studio page as another area of interaction for fans and users checking out the artists music. This was my favorite page to create and took quite some time! This page would function as a sort of photo diary where various images of Tuwop were set in an array and using the keyboard, users could watch them pop on the screen in random order and in random location. On top of this, I set keycodes for various audio snippets I uploaded that were part of the artists tracks so whenever a user presses a key on their keyboard, a new image would appear and a short sound from either the beat of a song (drum sample, vocal sample, etc), or a vocal sound would be played at the same time. When I user tested this page, I found that people spent quite a lot of time playing around with this feature on the website and it kept them on the site for longer periods of time! This was definitely the memorable experience I was hoping to create when designing this portion of the site for the artist! The code for the audio clips / keypress javascript function is below:

<audio data-key="113" id="Q" class="clip" src="gang.wav" preload="auto"></audio>
<audio data-key="119" id="W" class="clip" src="2.wav" preload="auto"></audio>
<audio data-key="101" id="E" class="clip" src="3.wav" preload="auto"></audio>
<audio data-key="97" id="A"  class="clip" src="gang2.wav" preload="auto"></audio>
<audio data-key="115" id="S" class="clip" src="5.wav" preload="auto"></audio>
<audio data-key="100" id="D" class="clip" src="6.wav" preload="auto"></audio>
<audio data-key="122" id="Z" class="clip" src="7.wav" preload="auto"></audio>
<audio data-key="120" id="X" class="clip" src="8.wav" preload="auto"></audio>
<audio data-key="99" class="clip" id="C" src="9.wav" preload="auto"></audio>
<audio data-key="32" class="clip" id="Space" src="10.wav" preload="auto"></audio>
<audio data-key="102" class="clip" id="F" src="11.wav" preload="auto"></audio>  

$(document).ready(function () {

    // keydown function
	$(document).keypress(function (e) {
        var code = e.keyCode;
        if (code == 113 || code == 119 || code == 101 || code == 97 || code == 115 || code == 100 || code == 122 || code == 120 || 
        	code == 99 || code == 32 || code == 39 || code == 102) {
			const audio = $("audio[data-key=" + code + "]");




Essentially what is going on above is in the main HTML file for this page, I preloaded the audio I wanted to use and using “data-key” property I set the individual keys I would use to play sound with individual audio files. Below, in my js file, basically so this function could occur anywhere on the page regardless of position of the preloaded audio on the HTML page, I used $(document) so that during a keypress, the function would scan the entire page for the pressed code not just one particular area/div/etc. The audio variable became a combination of the data-key from the HTML page and the code intrinsically given to the various keys used. Finally, after combining these codes with the if statement, the play method would play the audio given the keypress. Below is the code for the images being displayed on this page and their random positioning:

var pictures = new Array();
var count = 0;

                // Show an image
                var x = document.querySelector(".container");
                var bodyWidth = x.clientWidth;
  		 var bodyHeight = x.clientHeight;
                var new_img =  pictures[Math.floor(Math.random() * pictures.length)];
                var imgtag = $('<img id="randomImg">');
               	if(count == 10) {
               	$('img:first-child', this).remove();

 $('.imgs').each(function(idx, img) {
    var randPosX = Math.floor((Math.random() * bodyWidth));
    var randPosY = Math.floor((Math.random() * bodyHeight));
    $(imgtag).css('left', randPosX);
    $(imgtag).css('top', randPosY);



As you can see, I created an empty array and set the photos I wanted to include into the indices for the array. Simultaneously with the audio keypresses, any time a user pressed any key, the function for the photos would begin. I created the variables for width and height of the document for later purposes for the random positioning. The new_img variable would function to choose one of the random indices out of the array to display on each keypress. After appending the newly shown images to the page, the count which started at 0 at the beginning of the js file would iterate each keypress. Whenever there were 10 images being displayed on the screen at once, I would begin to remove the oldest one each new iteration to avoid overload as well as a cluttered screen. Lastly, I randomly positioned the newly displayed images by using the Math module and creating a calculation for X and Y positions for each image using this module and the bodyWidth and bodyHeight variables which were created earlier. Setting the css for the images based on this function allowed each keypress to display an image in a completely unique position.

Lastly, here is a video walking through the site showing the responsiveness for various browser sizes using media queries. To see more in-depth, you can view the code on my github here.