├── LICENSE ├── README.md ├── assets ├── css │ └── style.css ├── images │ ├── Thumbs.db │ ├── poster-1.jpg │ ├── poster-2.jpg │ ├── poster-3.jpg │ ├── poster-4.jpg │ └── poster-5.jpg ├── js │ └── script.js └── music │ ├── music-1.mp3 │ ├── music-2.mp3 │ ├── music-3.mp3 │ ├── music-4.mp3 │ └── music-5.mp3 ├── favicon.svg ├── index.html ├── index.txt ├── readme-images └── desktop.png └── style-guide.md /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Sadee 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |
2 | 3 | ![GitHub repo size](https://img.shields.io/github/repo-size/codewithsadee/music-player) 4 | ![GitHub stars](https://img.shields.io/github/stars/codewithsadee/music-player?style=social) 5 | ![GitHub forks](https://img.shields.io/github/forks/codewithsadee/music-player?style=social) 6 | [![Twitter Follow](https://img.shields.io/twitter/follow/codewithsadee_?style=social)](https://twitter.com/intent/follow?screen_name=codewithsadee_) 7 | [![YouTube Video Views](https://img.shields.io/youtube/views/jbMd2NVFrZk?style=social)](https://youtu.be/jbMd2NVFrZk) 8 | 9 |
10 |
11 | 12 |

Web Music Player

13 | 14 | A fully responsive web music player using vanilla javascript,
Responsive for all devices, build using html, css, and javascript. 15 | 16 | ➥ Live Demo 17 | 18 |
19 | 20 |
21 | 22 | ### Demo Screeshots 23 | 24 | ![Music Player Desktop Demo](./readme-images/desktop.png "Desktop Demo") 25 | 26 | ### Prerequisites 27 | 28 | Before you begin, ensure you have met the following requirements: 29 | 30 | * [Git](https://git-scm.com/downloads "Download Git") must be installed on your operating system. 31 | 32 | ### Run Locally 33 | 34 | To run **Music Player** locally, run this command on your git bash: 35 | 36 | Linux and macOS: 37 | 38 | ```bash 39 | sudo git clone https://github.com/codewithsadee/music-player.git 40 | ``` 41 | 42 | Windows: 43 | 44 | ```bash 45 | git clone https://github.com/codewithsadee/music-player.git 46 | ``` 47 | 48 | ### Contact 49 | 50 | If you want to contact with me you can reach me at [Twitter](https://www.twitter.com/codewithsadee). 51 | 52 | ### License 53 | 54 | This project is **free to use** and does not contains any license. 55 | -------------------------------------------------------------------------------- /assets/css/style.css: -------------------------------------------------------------------------------- 1 | /*-----------------------------------*\ 2 | #style.css 3 | \*-----------------------------------*/ 4 | 5 | /** 6 | * copyright 2022 codewithsadee 7 | */ 8 | 9 | 10 | 11 | 12 | 13 | /*-----------------------------------*\ 14 | #CUSTOM PROPERTY 15 | \*-----------------------------------*/ 16 | 17 | :root { 18 | 19 | /** 20 | * colors 21 | */ 22 | 23 | --eerie-black_a95: hsla(204, 9%, 11%, 0.95); 24 | --eerie-black_a50: hsla(204, 9%, 11%, 0.5); 25 | --eerie-black: hsl(204, 9%, 11%); 26 | --gainsboro: hsl(225, 7%, 89%); 27 | --charcoal: hsl(203, 9%, 28%); 28 | --silver-sand: hsl(208, 12%, 78%); 29 | --light-sky-blue: hsl(200, 100%, 73%); 30 | --prussian-blue: hsl(196, 100%, 14%); 31 | --black: hsl(0, 0%, 0%); 32 | --black_a50: hsla(0, 0%, 0%, 0.5); 33 | --black_a20: hsla(0, 0%, 0%, 0.2); 34 | --light-sky-blue_a8: hsla(200, 100%, 73%, 0.08); 35 | --light-sky-blue_a12: hsla(200, 100%, 73%, 0.12); 36 | --silver-sand_a8: hsla(208, 12%, 78%, 0.08); 37 | --silver-sand_a12: hsla(208, 12%, 78%, 0.12); 38 | 39 | --background: var(--eerie-black); 40 | --background-opacity: var(--eerie-black_a95); 41 | --on-background: var(--gainsboro); 42 | --surface-variant: var(--charcoal); 43 | --on-surface-variant: var(--silver-sand); 44 | --on-surface-variant-hover: var(--light-sky-blue_a8); 45 | --on-surface-variant-focus: var(--light-sky-blue_a12); 46 | --primary: var(--light-sky-blue); 47 | --on-primary: var(--prussian-blue); 48 | 49 | /** 50 | * gradient 51 | */ 52 | 53 | --gradient: linear-gradient(180deg, hsla(204, 9%, 11%, 0.90) 60%, transparent 120%); 54 | 55 | /** 56 | * typography 57 | */ 58 | 59 | --ff-inter: 'Inter', sans-serif; 60 | 61 | --headline-sm: 2.4rem; 62 | --title-lg: 2.2rem; 63 | --label-lg: 1.4rem; 64 | --label-md: 1.2rem; 65 | 66 | --fw-400: 400; 67 | --fw-500: 500; 68 | 69 | /** 70 | * box shadow 71 | */ 72 | 73 | --shadow-1: 0 1px 4px 1px var(--black_a20); 74 | --shadow-2: 0 1px 4px 1px var(--black_a50); 75 | 76 | /** 77 | * border radius 78 | */ 79 | 80 | --radius-24: 24px; 81 | --radius-16: 16px; 82 | --radius-12: 12px; 83 | --radius-pill: 100px; 84 | --radius-circle: 50%; 85 | 86 | /** 87 | * transition 88 | */ 89 | 90 | --transition-1: 200ms cubic-bezier(0.2, 0, 0, 1); 91 | --transition-2: 300ms cubic-bezier(0.2, 0, 0, 1); 92 | 93 | } 94 | 95 | 96 | 97 | 98 | 99 | /*-----------------------------------*\ 100 | #RESET 101 | \*-----------------------------------*/ 102 | 103 | *, 104 | *::before, 105 | *::after { 106 | margin: 0; 107 | padding: 0; 108 | box-sizing: border-box; 109 | } 110 | 111 | li { list-style: none; } 112 | 113 | a, 114 | img, 115 | span, 116 | input, 117 | button { display: block; } 118 | 119 | a { 120 | text-decoration: none; 121 | color: inherit; 122 | } 123 | 124 | img { height: auto; } 125 | 126 | input, 127 | button { 128 | background: none; 129 | border: none; 130 | font: inherit; 131 | } 132 | 133 | input { width: 100%; } 134 | 135 | button { cursor: pointer; } 136 | 137 | html { 138 | font-family: var(--ff-inter); 139 | font-size: 10px; 140 | scroll-behavior: smooth; 141 | } 142 | 143 | body { 144 | background-color: var(--black); 145 | color: var(--on-background); 146 | font-size: 1.6rem; 147 | line-height: 1.5; 148 | min-height: 100vh; 149 | min-width: 250px; 150 | background-image: url("../images/poster-1.jpg"); 151 | background-size: 150%; 152 | background-repeat: no-repeat; 153 | background-position: top; 154 | overflow: overlay; 155 | } 156 | 157 | body.modalActive { overflow: hidden; } 158 | 159 | ::-webkit-scrollbar { width: 8px; } 160 | 161 | ::-webkit-scrollbar-track { background: transparent; } 162 | 163 | ::-webkit-scrollbar-thumb { 164 | background-color: transparent; 165 | border-radius: var(--radius-pill); 166 | } 167 | 168 | body:hover::-webkit-scrollbar-thumb { background-color: var(--surface-variant); } 169 | 170 | 171 | 172 | 173 | 174 | /*-----------------------------------*\ 175 | #REUSED STYLE 176 | \*-----------------------------------*/ 177 | 178 | .material-symbols-rounded { 179 | font-variation-settings: 'FILL' 0, 'wght' 400, 'GRAD' -25, 'opsz' 24; 180 | width: 1em; 181 | height: 1em; 182 | overflow: hidden; 183 | user-select: none; 184 | } 185 | 186 | .wrapper { 187 | display: flex; 188 | align-items: center; 189 | } 190 | 191 | .title-lg { 192 | font-size: var(--title-lg); 193 | font-weight: var(--fw-400); 194 | } 195 | 196 | .btn-icon { 197 | color: var(--on-surface-variant); 198 | width: 40px; 199 | height: 40px; 200 | display: grid; 201 | place-items: center; 202 | border-radius: var(--radius-circle); 203 | } 204 | 205 | .btn-icon:hover { background-color: var(--on-surface-variant-hover); } 206 | 207 | .btn-icon:is(:focus, :focus-visible) { 208 | background-color: var(--on-surface-variant-focus); 209 | } 210 | 211 | .img-cover { 212 | width: 100%; 213 | height: 100%; 214 | object-fit: cover; 215 | } 216 | 217 | .headline-sm { 218 | font-size: var(--headline-sm); 219 | font-weight: var(--fw-400); 220 | } 221 | 222 | .label-lg, 223 | .label-md { font-weight: var(--fw-500); } 224 | 225 | .label-lg { 226 | font-size: var(--label-lg); 227 | letter-spacing: 0.1px; 228 | } 229 | 230 | .label-md { 231 | font-size: var(--label-md); 232 | letter-spacing: 0.5px; 233 | } 234 | 235 | 236 | 237 | 238 | 239 | /*-----------------------------------*\ 240 | #TOP APP BAR 241 | \*-----------------------------------*/ 242 | 243 | .top-bar { 244 | position: fixed; 245 | top: 0; 246 | left: 0; 247 | background-color: var(--background-opacity); 248 | min-width: 250px; 249 | width: 100%; 250 | height: 64px; 251 | padding-inline: 16px; 252 | justify-content: space-between; 253 | backdrop-filter: blur(50px); 254 | box-shadow: var(--shadow-1); 255 | z-index: 2; 256 | } 257 | 258 | .logo { gap: 12px; } 259 | 260 | 261 | 262 | 263 | 264 | /*-----------------------------------*\ 265 | #PLAYER 266 | \*-----------------------------------*/ 267 | 268 | .volume { display: none; } 269 | 270 | .player { 271 | --padding: 24px; 272 | background-image: var(--gradient); 273 | padding: var(--padding); 274 | padding-block-start: calc(64px + var(--padding)); 275 | min-height: 100vh; 276 | display: flex; 277 | text-align: center; 278 | backdrop-filter: blur(30px); 279 | overflow: hidden; 280 | } 281 | 282 | .player .container { 283 | width: 100%; 284 | display: grid; 285 | grid-template-rows: 1fr max-content; 286 | gap: 24px; 287 | } 288 | 289 | .music-banner { 290 | aspect-ratio: 1 / 1; 291 | background-color: var(--surface-variant); 292 | max-width: 400px; 293 | margin-inline: auto; 294 | align-self: center; 295 | overflow: hidden; 296 | border-radius: var(--radius-24); 297 | } 298 | 299 | .label-wrapper { 300 | justify-content: center; 301 | opacity: 0.8; 302 | margin-block: 8px 4px; 303 | } 304 | 305 | .label-wrapper span:first-child::after { 306 | content: "|"; 307 | display: inline-block; 308 | margin-inline: 4px; 309 | } 310 | 311 | .artist { 312 | opacity: 0.6; 313 | margin-block-end: 36px; 314 | } 315 | 316 | .seek-control { margin-block-end: 20px; } 317 | 318 | .range-wrapper { position: relative; } 319 | 320 | .range { 321 | appearance: none; 322 | cursor: pointer; 323 | } 324 | 325 | .range::-webkit-slider-runnable-track { 326 | appearance: none; 327 | background-color: var(--surface-variant); 328 | height: 6px; 329 | width: 100%; 330 | border-radius: var(--radius-pill); 331 | } 332 | 333 | .range-fill { 334 | content: ""; 335 | position: absolute; 336 | top: 0; 337 | left: 0; 338 | width: 0; 339 | height: 6px; 340 | background-color: var(--primary); 341 | border-radius: var(--radius-pill); 342 | pointer-events: none; 343 | } 344 | 345 | .range::-webkit-slider-thumb { 346 | appearance: none; 347 | background-color: var(--primary); 348 | width: 16px; 349 | height: 16px; 350 | margin-block-start: -5px; 351 | border-radius: var(--radius-pill); 352 | transition: var(--transition-1); 353 | } 354 | 355 | .range::-webkit-slider-thumb:hover { 356 | box-shadow: 0 0 0 12px var(--on-surface-variant-hover); 357 | } 358 | 359 | .range::-webkit-slider-thumb:is(:focus, :focus-visible) { 360 | box-shadow: 0 0 0 12px var(--on-surface-variant-focus); 361 | } 362 | 363 | .duration-label { 364 | justify-content: space-between; 365 | margin-block-start: 12px; 366 | } 367 | 368 | .player-control { justify-content: space-evenly; } 369 | 370 | .player-control .toggle.active { color: var(--primary); } 371 | 372 | .player-control .play { 373 | background-color: var(--surface-variant); 374 | color: var(--primary); 375 | } 376 | 377 | .player-control .play.active { 378 | background-color: var(--primary); 379 | color: var(--on-primary); 380 | } 381 | 382 | .player-control .btn-icon.active .default-icon, 383 | .player-control .btn-icon .active-icon { display: none; } 384 | 385 | .player-control .btn-icon .default-icon, 386 | .player-control .btn-icon.active .active-icon { display: block; } 387 | 388 | 389 | 390 | 391 | 392 | /*-----------------------------------*\ 393 | #PLAYLIST 394 | \*-----------------------------------*/ 395 | 396 | .playlist { 397 | position: fixed; 398 | top: 0; 399 | right: -180px; 400 | width: 180px; 401 | height: 100vh; 402 | background-color: var(--background); 403 | padding: 28px; 404 | border-top-left-radius: var(--radius-16); 405 | border-bottom-left-radius: var(--radius-16); 406 | box-shadow: var(--shadow-2); 407 | overflow-y: overlay; 408 | visibility: hidden; 409 | transition: var(--transition-2); 410 | z-index: 4; 411 | } 412 | 413 | .playlist:hover::-webkit-scrollbar-thumb { background-color: var(--surface-variant); } 414 | 415 | .playlist.active { 416 | transform: translateX(-180px); 417 | visibility: visible; 418 | } 419 | 420 | .music-list { 421 | display: grid; 422 | gap: 20px; 423 | } 424 | 425 | .music-item { 426 | position: relative; 427 | border-radius: var(--radius-12); 428 | background-color: var(--surface-variant); 429 | overflow: hidden; 430 | } 431 | 432 | .music-item .item-icon { 433 | position: absolute; 434 | inset: 0; 435 | background-color: var(--eerie-black_a50); 436 | display: grid; 437 | place-items: center; 438 | opacity: 0; 439 | } 440 | 441 | .music-item.playing .item-icon { opacity: 1; } 442 | 443 | .music-item .item-icon .material-symbols-rounded { 444 | color: var(--primary); 445 | font-size: 3rem; 446 | } 447 | 448 | .music-item:is(:hover, :focus-visible, :active, .playing) { 449 | outline: 1px solid var(--primary); 450 | } 451 | 452 | .overlay { 453 | position: fixed; 454 | top: 0; 455 | left: 0; 456 | width: 100%; 457 | height: 100vh; 458 | background-color: var(--black); 459 | opacity: 0; 460 | pointer-events: none; 461 | transition: var(--transition-2); 462 | z-index: 3; 463 | } 464 | 465 | .overlay.active { 466 | pointer-events: all; 467 | opacity: 0.5; 468 | } 469 | 470 | 471 | 472 | 473 | 474 | /*-----------------------------------*\ 475 | #MEDIA QUERIES 476 | \*-----------------------------------*/ 477 | 478 | /** 479 | * responsive for large than 575px screen 480 | */ 481 | 482 | @media (min-width: 575px) { 483 | 484 | /** 485 | * RESET 486 | */ 487 | 488 | body { background-size: 100%; } 489 | 490 | 491 | 492 | /** 493 | * PLAYER 494 | */ 495 | 496 | .player { justify-content: center; } 497 | 498 | .player .container { max-width: 540px; } 499 | 500 | } 501 | 502 | 503 | 504 | 505 | 506 | /** 507 | * responsive for large than 992px screen 508 | */ 509 | 510 | @media (min-width: 992px) { 511 | 512 | /** 513 | * CUSTOM PROPERTY 514 | */ 515 | 516 | :root { 517 | 518 | /** 519 | * typography 520 | */ 521 | 522 | --headline-sm: 4.2rem; 523 | --label-lg: 2.2rem; 524 | 525 | } 526 | 527 | 528 | 529 | /** 530 | * RESET 531 | */ 532 | 533 | body { 534 | background-size: 40% 100%; 535 | background-position: left center; 536 | } 537 | 538 | article { display: flex; } 539 | 540 | 541 | 542 | /** 543 | * REUSED STYLE 544 | */ 545 | 546 | .btn-icon { 547 | width: 54px; 548 | height: 54px; 549 | } 550 | 551 | .btn-icon .material-symbols-rounded { font-size: 2.8rem; } 552 | 553 | 554 | 555 | /** 556 | * TOP APP BAR 557 | */ 558 | 559 | .top-bar-actions { display: none; } 560 | 561 | 562 | 563 | /** 564 | * PLAYER 565 | */ 566 | 567 | .player { 568 | --padding: 48px; 569 | text-align: left; 570 | flex-grow: 1; 571 | align-items: center; 572 | backdrop-filter: blur(100px); 573 | } 574 | 575 | .player .container { 576 | max-width: 1200px; 577 | grid-template-columns: 0.8fr 1fr; 578 | grid-template-rows: 1fr; 579 | gap: 48px; 580 | max-height: 500px; 581 | height: 100%; 582 | } 583 | 584 | .music-banner { 585 | aspect-ratio: unset; 586 | max-width: max-content; 587 | width: 100%; 588 | height: 100%; 589 | } 590 | 591 | .music-content { 592 | display: flex; 593 | flex-direction: column; 594 | padding-block-start: 48px; 595 | min-width: 100%; 596 | } 597 | 598 | .music-content :is(.headline-sm, .label-lg, .label-md) { 599 | max-width: 85%; 600 | } 601 | 602 | .label-wrapper { 603 | justify-content: flex-start; 604 | margin-block-end: 8px; 605 | } 606 | 607 | .artist { --label-md: 1.8rem; } 608 | 609 | .seek-control { 610 | margin-block-start: auto; 611 | display: grid; 612 | grid-template-columns: 1fr 150px; 613 | align-items: center; 614 | gap: 24px; 615 | } 616 | 617 | .volume { 618 | display: flex; 619 | align-items: center; 620 | gap: 4px; 621 | margin-block-start: -30px; 622 | } 623 | 624 | .volume .btn-icon { flex-shrink: 0; } 625 | 626 | .volume .range-fill { width: 100%; } 627 | 628 | .player-control { 629 | margin-inline-end: 174px; 630 | min-width: max-content; 631 | gap: 8px; 632 | } 633 | 634 | 635 | 636 | /** 637 | * PLAYLIST 638 | */ 639 | 640 | .overlay { display: none; } 641 | 642 | .playlist { 643 | position: static; 644 | visibility: visible; 645 | border-radius: 0; 646 | box-shadow: none; 647 | flex-shrink: 0; 648 | } 649 | 650 | .playlist.active { transform: unset; } 651 | 652 | .music-item:is(:hover, :focus-visible, :active, .playing) { 653 | outline: 2px solid var(--primary); 654 | } 655 | 656 | .music-item .item-icon .material-symbols-rounded { 657 | font-size: 3.6rem; 658 | } 659 | 660 | } 661 | 662 | 663 | 664 | 665 | 666 | /** 667 | * responsive for large than 1200px screen 668 | */ 669 | 670 | @media (min-width: 1200px) { 671 | 672 | /** 673 | * PLAYLIST 674 | */ 675 | 676 | .playlist { 677 | padding: 32px; 678 | width: 220px; 679 | } 680 | 681 | .music-list { gap: 28px; } 682 | 683 | } -------------------------------------------------------------------------------- /assets/images/Thumbs.db: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codewithsadee/music-player/1e1ffb4d6fad2cea6ba3e604ccddcc0d68f3dc4f/assets/images/Thumbs.db -------------------------------------------------------------------------------- /assets/images/poster-1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codewithsadee/music-player/1e1ffb4d6fad2cea6ba3e604ccddcc0d68f3dc4f/assets/images/poster-1.jpg -------------------------------------------------------------------------------- /assets/images/poster-2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codewithsadee/music-player/1e1ffb4d6fad2cea6ba3e604ccddcc0d68f3dc4f/assets/images/poster-2.jpg -------------------------------------------------------------------------------- /assets/images/poster-3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codewithsadee/music-player/1e1ffb4d6fad2cea6ba3e604ccddcc0d68f3dc4f/assets/images/poster-3.jpg -------------------------------------------------------------------------------- /assets/images/poster-4.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codewithsadee/music-player/1e1ffb4d6fad2cea6ba3e604ccddcc0d68f3dc4f/assets/images/poster-4.jpg -------------------------------------------------------------------------------- /assets/images/poster-5.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codewithsadee/music-player/1e1ffb4d6fad2cea6ba3e604ccddcc0d68f3dc4f/assets/images/poster-5.jpg -------------------------------------------------------------------------------- /assets/js/script.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | 4 | 5 | /** 6 | * all music information 7 | */ 8 | 9 | const musicData = [ 10 | { 11 | backgroundImage: "./assets/images/poster-1.jpg", 12 | posterUrl: "./assets/images/poster-1.jpg", 13 | title: "Happy Moments (Master)", 14 | album: "No Spirit", 15 | year: 2022, 16 | artist: "No Spirit x Tonion", 17 | musicPath: "./assets/music/music-1.mp3", 18 | }, 19 | { 20 | backgroundImage: "./assets/images/poster-2.jpg", 21 | posterUrl: "./assets/images/poster-2.jpg", 22 | title: "We Are Going To Be Ok (Master)", 23 | album: "No Spirit", 24 | year: 2022, 25 | artist: "No Spirit x Jhove", 26 | musicPath: "./assets/music/music-2.mp3", 27 | }, 28 | { 29 | backgroundImage: "./assets/images/poster-3.jpg", 30 | posterUrl: "./assets/images/poster-3.jpg", 31 | title: "Winter Meadow", 32 | album: "No Spirit", 33 | year: 2022, 34 | artist: "No Spirit x juniorodeo", 35 | musicPath: "./assets/music/music-3.mp3", 36 | }, 37 | { 38 | backgroundImage: "./assets/images/poster-4.jpg", 39 | posterUrl: "./assets/images/poster-4.jpg", 40 | title: "From Where We Started", 41 | album: "No Spirit", 42 | year: 2022, 43 | artist: "No Spirit", 44 | musicPath: "./assets/music/music-4.mp3", 45 | }, 46 | { 47 | backgroundImage: "./assets/images/poster-5.jpg", 48 | posterUrl: "./assets/images/poster-5.jpg", 49 | title: "Where I Found You", 50 | album: "No Spirit", 51 | year: 2022, 52 | artist: "No Spirit", 53 | musicPath: "./assets/music/music-5.mp3", 54 | }, 55 | ]; 56 | 57 | 58 | 59 | /** 60 | * add eventListnere on all elements that are passed 61 | */ 62 | 63 | const addEventOnElements = function (elements, eventType, callback) { 64 | for (let i = 0, len = elements.length; i < len; i++) { 65 | elements[i].addEventListener(eventType, callback); 66 | } 67 | } 68 | 69 | 70 | 71 | /** 72 | * PLAYLIST 73 | * 74 | * add all music in playlist, from 'musicData' 75 | */ 76 | 77 | const playlist = document.querySelector("[data-music-list]"); 78 | 79 | for (let i = 0, len = musicData.length; i < len; i++) { 80 | playlist.innerHTML += ` 81 |
  • 82 | 90 |
  • 91 | `; 92 | } 93 | 94 | 95 | 96 | /** 97 | * PLAYLIST MODAL SIDEBAR TOGGLE 98 | * 99 | * show 'playlist' modal sidebar when click on playlist button in top app bar 100 | * and hide when click on overlay or any playlist-item 101 | */ 102 | 103 | const playlistSideModal = document.querySelector("[data-playlist]"); 104 | const playlistTogglers = document.querySelectorAll("[data-playlist-toggler]"); 105 | const overlay = document.querySelector("[data-overlay]"); 106 | 107 | const togglePlaylist = function () { 108 | playlistSideModal.classList.toggle("active"); 109 | overlay.classList.toggle("active"); 110 | document.body.classList.toggle("modalActive"); 111 | } 112 | 113 | addEventOnElements(playlistTogglers, "click", togglePlaylist); 114 | 115 | 116 | 117 | /** 118 | * PLAYLIST ITEM 119 | * 120 | * remove active state from last time played music 121 | * and add active state in clicked music 122 | */ 123 | 124 | const playlistItems = document.querySelectorAll("[data-playlist-item]"); 125 | 126 | let currentMusic = 0; 127 | let lastPlayedMusic = 0; 128 | 129 | const changePlaylistItem = function () { 130 | playlistItems[lastPlayedMusic].classList.remove("playing"); 131 | playlistItems[currentMusic].classList.add("playing"); 132 | } 133 | 134 | addEventOnElements(playlistItems, "click", function () { 135 | lastPlayedMusic = currentMusic; 136 | currentMusic = Number(this.dataset.playlistItem); 137 | changePlaylistItem(); 138 | }); 139 | 140 | 141 | 142 | /** 143 | * PLAYER 144 | * 145 | * change all visual information on player, based on current music 146 | */ 147 | 148 | const playerBanner = document.querySelector("[data-player-banner]"); 149 | const playerTitle = document.querySelector("[data-title]"); 150 | const playerAlbum = document.querySelector("[data-album]"); 151 | const playerYear = document.querySelector("[data-year]"); 152 | const playerArtist = document.querySelector("[data-artist]"); 153 | 154 | const audioSource = new Audio(musicData[currentMusic].musicPath); 155 | 156 | const changePlayerInfo = function () { 157 | playerBanner.src = musicData[currentMusic].posterUrl; 158 | playerBanner.setAttribute("alt", `${musicData[currentMusic].title} Album Poster`); 159 | document.body.style.backgroundImage = `url(${musicData[currentMusic].backgroundImage})`; 160 | playerTitle.textContent = musicData[currentMusic].title; 161 | playerAlbum.textContent = musicData[currentMusic].album; 162 | playerYear.textContent = musicData[currentMusic].year; 163 | playerArtist.textContent = musicData[currentMusic].artist; 164 | 165 | audioSource.src = musicData[currentMusic].musicPath; 166 | 167 | audioSource.addEventListener("loadeddata", updateDuration); 168 | playMusic(); 169 | } 170 | 171 | addEventOnElements(playlistItems, "click", changePlayerInfo); 172 | 173 | /** update player duration */ 174 | const playerDuration = document.querySelector("[data-duration]"); 175 | const playerSeekRange = document.querySelector("[data-seek]"); 176 | 177 | /** pass seconds and get timcode formate */ 178 | const getTimecode = function (duration) { 179 | const minutes = Math.floor(duration / 60); 180 | const seconds = Math.ceil(duration - (minutes * 60)); 181 | const timecode = `${minutes}:${seconds < 10 ? "0" : ""}${seconds}`; 182 | return timecode; 183 | } 184 | 185 | const updateDuration = function () { 186 | playerSeekRange.max = Math.ceil(audioSource.duration); 187 | playerDuration.textContent = getTimecode(Number(playerSeekRange.max)); 188 | } 189 | 190 | audioSource.addEventListener("loadeddata", updateDuration); 191 | 192 | 193 | 194 | /** 195 | * PLAY MUSIC 196 | * 197 | * play and pause music when click on play button 198 | */ 199 | 200 | const playBtn = document.querySelector("[data-play-btn]"); 201 | 202 | let playInterval; 203 | 204 | const playMusic = function () { 205 | if (audioSource.paused) { 206 | audioSource.play(); 207 | playBtn.classList.add("active"); 208 | playInterval = setInterval(updateRunningTime, 500); 209 | } else { 210 | audioSource.pause(); 211 | playBtn.classList.remove("active"); 212 | clearInterval(playInterval); 213 | } 214 | } 215 | 216 | playBtn.addEventListener("click", playMusic); 217 | 218 | 219 | /** update running time while playing music */ 220 | 221 | const playerRunningTime = document.querySelector("[data-running-time"); 222 | 223 | const updateRunningTime = function () { 224 | playerSeekRange.value = audioSource.currentTime; 225 | playerRunningTime.textContent = getTimecode(audioSource.currentTime); 226 | 227 | updateRangeFill(); 228 | isMusicEnd(); 229 | } 230 | 231 | 232 | 233 | /** 234 | * RANGE FILL WIDTH 235 | * 236 | * change 'rangeFill' width, while changing range value 237 | */ 238 | 239 | const ranges = document.querySelectorAll("[data-range]"); 240 | const rangeFill = document.querySelector("[data-range-fill]"); 241 | 242 | const updateRangeFill = function () { 243 | let element = this || ranges[0]; 244 | 245 | const rangeValue = (element.value / element.max) * 100; 246 | element.nextElementSibling.style.width = `${rangeValue}%`; 247 | } 248 | 249 | addEventOnElements(ranges, "input", updateRangeFill); 250 | 251 | 252 | 253 | /** 254 | * SEEK MUSIC 255 | * 256 | * seek music while changing player seek range 257 | */ 258 | 259 | const seek = function () { 260 | audioSource.currentTime = playerSeekRange.value; 261 | playerRunningTime.textContent = getTimecode(playerSeekRange.value); 262 | } 263 | 264 | playerSeekRange.addEventListener("input", seek); 265 | 266 | 267 | 268 | /** 269 | * END MUSIC 270 | */ 271 | 272 | const isMusicEnd = function () { 273 | if (audioSource.ended) { 274 | playBtn.classList.remove("active"); 275 | audioSource.currentTime = 0; 276 | playerSeekRange.value = audioSource.currentTime; 277 | playerRunningTime.textContent = getTimecode(audioSource.currentTime); 278 | updateRangeFill(); 279 | } 280 | } 281 | 282 | 283 | 284 | /** 285 | * SKIP TO NEXT MUSIC 286 | */ 287 | 288 | const playerSkipNextBtn = document.querySelector("[data-skip-next]"); 289 | 290 | const skipNext = function () { 291 | lastPlayedMusic = currentMusic; 292 | 293 | if (isShuffled) { 294 | shuffleMusic(); 295 | } else { 296 | currentMusic >= musicData.length - 1 ? currentMusic = 0 : currentMusic++; 297 | } 298 | 299 | changePlayerInfo(); 300 | changePlaylistItem(); 301 | } 302 | 303 | playerSkipNextBtn.addEventListener("click", skipNext); 304 | 305 | 306 | 307 | /** 308 | * SKIP TO PREVIOUS MUSIC 309 | */ 310 | 311 | const playerSkipPrevBtn = document.querySelector("[data-skip-prev]"); 312 | 313 | const skipPrev = function () { 314 | lastPlayedMusic = currentMusic; 315 | 316 | if (isShuffled) { 317 | shuffleMusic(); 318 | } else { 319 | currentMusic <= 0 ? currentMusic = musicData.length - 1 : currentMusic--; 320 | } 321 | 322 | changePlayerInfo(); 323 | changePlaylistItem(); 324 | } 325 | 326 | playerSkipPrevBtn.addEventListener("click", skipPrev); 327 | 328 | 329 | 330 | /** 331 | * SHUFFLE MUSIC 332 | */ 333 | 334 | /** get random number for shuffle */ 335 | const getRandomMusic = () => Math.floor(Math.random() * musicData.length); 336 | 337 | const shuffleMusic = () => currentMusic = getRandomMusic(); 338 | 339 | const playerShuffleBtn = document.querySelector("[data-shuffle]"); 340 | let isShuffled = false; 341 | 342 | const shuffle = function () { 343 | playerShuffleBtn.classList.toggle("active"); 344 | 345 | isShuffled = isShuffled ? false : true; 346 | } 347 | 348 | playerShuffleBtn.addEventListener("click", shuffle); 349 | 350 | 351 | 352 | /** 353 | * REPEAT MUSIC 354 | */ 355 | 356 | const playerRepeatBtn = document.querySelector("[data-repeat]"); 357 | 358 | const repeat = function () { 359 | if (!audioSource.loop) { 360 | audioSource.loop = true; 361 | this.classList.add("active"); 362 | } else { 363 | audioSource.loop = false; 364 | this.classList.remove("active"); 365 | } 366 | } 367 | 368 | playerRepeatBtn.addEventListener("click", repeat); 369 | 370 | 371 | 372 | /** 373 | * MUSIC VOLUME 374 | * 375 | * increase or decrease music volume when change the volume range 376 | */ 377 | 378 | const playerVolumeRange = document.querySelector("[data-volume]"); 379 | const playerVolumeBtn = document.querySelector("[data-volume-btn]"); 380 | 381 | const changeVolume = function () { 382 | audioSource.volume = playerVolumeRange.value; 383 | audioSource.muted = false; 384 | 385 | if (audioSource.volume <= 0.1) { 386 | playerVolumeBtn.children[0].textContent = "volume_mute"; 387 | } else if (audioSource.volume <= 0.5) { 388 | playerVolumeBtn.children[0].textContent = "volume_down"; 389 | } else { 390 | playerVolumeBtn.children[0].textContent = "volume_up"; 391 | } 392 | } 393 | 394 | playerVolumeRange.addEventListener("input", changeVolume); 395 | 396 | 397 | /** 398 | * MUTE MUSIC 399 | */ 400 | 401 | const muteVolume = function () { 402 | if (!audioSource.muted) { 403 | audioSource.muted = true; 404 | playerVolumeBtn.children[0].textContent = "volume_off"; 405 | } else { 406 | changeVolume(); 407 | } 408 | } 409 | 410 | playerVolumeBtn.addEventListener("click", muteVolume); -------------------------------------------------------------------------------- /assets/music/music-1.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codewithsadee/music-player/1e1ffb4d6fad2cea6ba3e604ccddcc0d68f3dc4f/assets/music/music-1.mp3 -------------------------------------------------------------------------------- /assets/music/music-2.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codewithsadee/music-player/1e1ffb4d6fad2cea6ba3e604ccddcc0d68f3dc4f/assets/music/music-2.mp3 -------------------------------------------------------------------------------- /assets/music/music-3.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codewithsadee/music-player/1e1ffb4d6fad2cea6ba3e604ccddcc0d68f3dc4f/assets/music/music-3.mp3 -------------------------------------------------------------------------------- /assets/music/music-4.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codewithsadee/music-player/1e1ffb4d6fad2cea6ba3e604ccddcc0d68f3dc4f/assets/music/music-4.mp3 -------------------------------------------------------------------------------- /assets/music/music-5.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codewithsadee/music-player/1e1ffb4d6fad2cea6ba3e604ccddcc0d68f3dc4f/assets/music/music-5.mp3 -------------------------------------------------------------------------------- /favicon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 12 | Music Player 13 | 14 | 15 | 16 | 19 | 20 | 21 | 24 | 25 | 26 | 27 | 28 | 31 | 33 | 34 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 46 | 47 |
    48 | 49 | 54 | 55 |
    56 | 59 |
    60 | 61 |
    62 | 63 | 64 | 65 | 66 | 67 |
    68 |
    69 | 70 | 73 | 74 |
    75 |
    76 | 77 |
    78 | Happy Moments (Master) Album Poster 80 |
    81 | 82 |
    83 | 84 |

    Happy Moments (Master)

    85 | 86 |

    87 | No Spirit 88 | 89 | 2022 90 |

    91 | 92 |

    No Spirit x Tonion

    93 | 94 |
    95 | 96 |
    97 |
    98 | 99 | 100 |
    101 |
    102 | 103 |
    104 | 0:00 105 | 106 | 1:00 107 |
    108 |
    109 | 110 |
    111 | 114 | 115 |
    116 | 117 | 118 |
    119 |
    120 |
    121 | 122 |
    123 | 124 |
    125 | 126 | 130 | 131 | 134 | 135 | 139 | 140 | 143 | 144 | 147 | 148 |
    149 | 150 |
    151 | 152 |
    153 |
    154 | 155 | 156 | 157 | 158 | 159 | 162 | 163 |
    164 | 165 |
      166 | 167 |
      168 | 169 |
      170 | 171 |
      172 |
      173 | 174 | 175 | 176 | 177 | 178 | 181 | 182 | 183 | 184 | 185 | -------------------------------------------------------------------------------- /index.txt: -------------------------------------------------------------------------------- 1 | Music Player 2 | 3 | A web music player html template made by codewithsadee 4 | 5 | 6 | 7 | #---------- TOP APP BAR ----------# 8 | 9 | graphic_eq 10 | 11 | Music Player 12 | 13 | queue_music 14 | 15 | 16 | 17 | #---------- PLAYER ----------# 18 | 19 | alt = Happy Moments (Master) Album Poster 20 | 21 | Happy Moments (Master) 22 | 23 | No Spirit 24 | 2022 25 | 26 | No Spirit x Tonion 27 | 28 | 0:00 29 | 1:00 30 | 31 | volume_up 32 | 33 | repeat 34 | repeat_one 35 | 36 | skip_previous 37 | 38 | play_arrow 39 | pause 40 | 41 | skip_next 42 | 43 | shuffle 44 | 45 | 46 | 47 | #---------- PLAYLIST ----------# 48 | 49 | alt = Happy Moments (Master) Album Poster 50 | 51 | equalizer -------------------------------------------------------------------------------- /readme-images/desktop.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codewithsadee/music-player/1e1ffb4d6fad2cea6ba3e604ccddcc0d68f3dc4f/readme-images/desktop.png -------------------------------------------------------------------------------- /style-guide.md: -------------------------------------------------------------------------------- 1 | # Essential Stuff 2 | 3 | ## Html import links 4 | 5 | Google font 6 | 7 | ``` html 8 | 9 | 10 | 11 | ``` 12 | 13 | Material icon 14 | 15 | ``` html 16 | 17 | ``` 18 | 19 | --- 20 | 21 | ## Colors 22 | 23 | ``` css 24 | --eerie-black_a95: hsla(204, 9%, 11%, 0.95); 25 | --eerie-black_a50: hsla(204, 9%, 11%, 0.5); 26 | --eerie-black: hsl(204, 9%, 11%); 27 | --gainsboro: hsl(225, 7%, 89%); 28 | --charcoal: hsl(203, 9%, 28%); 29 | --silver-sand: hsl(208, 12%, 78%); 30 | --light-sky-blue: hsl(200, 100%, 73%); 31 | --prussian-blue: hsl(196, 100%, 14%); 32 | --black: hsl(0, 0%, 0%); 33 | --black_a50: hsla(0, 0%, 0%, 0.5); 34 | --black_a20: hsla(0, 0%, 0%, 0.2); 35 | --light-sky-blue_a8: hsla(200, 100%, 73%, 0.08); 36 | --light-sky-blue_a12: hsla(200, 100%, 73%, 0.12); 37 | --silver-sand_a8: hsla(208, 12%, 78%, 0.08); 38 | --silver-sand_a12: hsla(208, 12%, 78%, 0.12); 39 | 40 | --background: var(--eerie-black); 41 | --background-opacity: var(--eerie-black_a95); 42 | --on-background: var(--gainsboro); 43 | --surface-variant: var(--charcoal); 44 | --on-surface-variant: var(--silver-sand); 45 | --on-surface-variant-hover: var(--light-sky-blue_a8); 46 | --on-surface-variant-focus: var(--light-sky-blue_a12); 47 | --primary: var(--light-sky-blue); 48 | --on-primary: var(--prussian-blue); 49 | ``` 50 | 51 | ## Gradient color 52 | 53 | ``` css 54 | --gradient: linear-gradient(180deg, hsla(204, 9%, 11%, 0.90) 60%, transparent 120%); 55 | ``` 56 | 57 | ## Typography 58 | 59 | ``` css 60 | --ff-inter: 'Inter', sans-serif; 61 | 62 | --headline-sm: 2.4rem; 63 | --title-lg: 2.2rem; 64 | --label-lg: 1.4rem; 65 | --label-md: 1.2rem; 66 | 67 | --fw-400: 400; 68 | --fw-500: 500; 69 | ``` 70 | 71 | ## Shadow 72 | 73 | ``` css 74 | --shadow-1: 0 1px 4px 1px var(--black_a20); 75 | --shadow-2: 0 1px 4px 1px var(--black_a50); 76 | ``` 77 | 78 | ## Border Radius 79 | 80 | ``` css 81 | --radius-24: 24px; 82 | --radius-16: 16px; 83 | --radius-12: 12px; 84 | --radius-pill: 100px; 85 | --radius-circle: 50%; 86 | ``` 87 | 88 | ## Transition 89 | 90 | ``` css 91 | --transition-1: 200ms cubic-bezier(0.2, 0, 0, 1); 92 | --transition-2: 300ms cubic-bezier(0.2, 0, 0, 1); 93 | ``` 94 | --------------------------------------------------------------------------------