How To Build A Music Manager With Nuxt.js And Express.js

Original Source: https://www.smashingmagazine.com/2020/02/music-manager-nuxtjs-expressjs/

How To Build A Music Manager With Nuxt.js And Express.js

How To Build A Music Manager With Nuxt.js And Express.js

Deven Rathore

2020-02-27T13:00:00+00:00
2020-02-27T18:37:26+00:00

Handling digital media assets such as audio and video in your application can be tricky because of the considerations that have to be made server-side (e.g. networking, storage and the asynchronous nature of handling file uploads). However, we can use libraries like Multer and Express.js to simplify our workflow on the backend while using Nuxt.js (Vue framework) to build out the front-end interactions.

Whenever a web client uploads a file to a server, it is generally submitted through a form and encoded as multipart/form-data. Multer is a middleware for Express.js and Node.js that makes it easy to handle this so-called multipart/form-data whenever your users upload files. In this tutorial, I will explain how you can build a music manager app by using Express.js with Multer to upload music and Nuxt.js (Vue framework) for our frontend.

Prerequisites

Familiarity with HTML, CSS, and JavaScript (ES6+);
Node.js, npm and MongoDB installed on your development machine;
VS code or any code editor of your choice;
Basic Knowledge of Express.js.

Building The Back-End Service

Let’s start by creating a directory for our project by navigating into the directory, and issuing npm init -y on your terminal to create a package.json file that manages all the dependencies for our application.

mkdir serverside && cd serverside
npm init -y

Next, install multer, express, and the other dependencies necessary to Bootstrap an Express.js app.

npm install express multer nodemon mongoose cors morgan body-parser –save

Next, create an index.js file:

touch index.js

Then, in the index.js file, we will initialize all the modules, create an Express.js app, and create a server for connecting to browsers:

const express = require(“express”);
const PORT = process.env.PORT || 4000;
const morgan = require(“morgan”);
const cors = require(“cors”);
const bodyParser = require(“body-parser”);
const mongoose = require(“mongoose”);
const config = require(“./config/db”);
const app = express();
//configure database and mongoose
mongoose.set(“useCreateIndex”, true);
mongoose
.connect(config.database, { useNewUrlParser: true })
.then(() => {
console.log(“Database is connected”);
})
.catch(err => {
console.log({ database_error: err });
});
// db configuaration ends here
//registering cors
app.use(cors());
//configure body parser
app.use(bodyParser.urlencoded({ extended: false }));
app.use(bodyParser.json());
//configure body-parser ends here
app.use(morgan(“dev”)); // configire morgan
// define first route
app.get(“/”, (req, res) => {
res.json(“Hola MEVN devs…Assemble”);
});
app.listen(PORT, () => {
console.log(`App is running on ${PORT}`);
});

We, first of all, bring in Express.js into the project and then define a port that our application will be running on. Next, we bring in the body-parser, morgan ,mongoose and the cors dependencies.

We then save the express instance in a variable called app. We can use the app instance to configure middleware in our application just as we configured the cors middleware. We also use the app instance to set up the root route that will run in the port we defined.

Let’s now create a /config folder for our database config and multer config:

mkdir config and cd config
touch multer.js && touch db.js

Then open config/db.js and add the following code to configure our database:

module.exports = {
database: “mongodb://localhost:27017/”,
secret: “password”
};

(This is actually an object that holds the database URL and the database secret.)

Running nodemon and navigating to localhost:4000 on your browser should give you this message:

“Hola MEVN devs…Assemble”

Also, this is what your terminal should now look like:

Running Nodemon using Terminal

Terminal preview (Large preview)

Setting Up Model, Routes, And Controllers

Let’s set up a file structure by typing in the following:

mkdir api && cd api
mkdir model && cd model && touch Music.js
cd ..
mkdir controller && cd controller && touch musicController.js
cd ..
mkdir routes && cd routes && touch music.js

In our terminal, we use mkdir to create a new directory, and then cd to move into a directory. So we start by creating a directory called api and then move into the api directory.

The touch command is used to create a new file inside a directory using the terminal, while the cd command is used to move out of a directory.

Now let’s head on over to our api/model/Music.js file to create a music schema. A model is a class with which we construct documents. In this case, each document will be a piece of music with properties and behaviors as declared in our schema:

let mongoose = require(“mongoose”);
let musicSchema = mongoose.Schema({
title: {
type: String,
required: true
},
music: {
type: Object,
required: true
},
artist: {
type: String,
required: true
},
created: {
type: Date,
default: Date.now()
}
});
let Music = mongoose.model(“Music”, musicSchema);
module.exports = Music;

Let’s head over to config/multer to configure Multer:

let multer = require(“multer”);
const path = require(“path”);
const storage = multer.diskStorage({
destination: (req, res, cb) => {
cb(null, “./uploads”);
},
filename: (req, file, cb) => {
cb(null, new Date().toISOString() + file.originalname);
}
});
const fileFilter = (req, file, cb) => {
if (
file.mimetype === “audio/mpeg” ||
file.mimetype === “audio/wave” ||
file.mimetype === “audio/wav” ||
file.mimetype === “audio/mp3”
) {
cb(null, true);
} else {
cb(null, false);
}
};
exports.upload = multer({
storage: storage,
limits: {
fileSize: 1024 * 1024 * 5
},
fileFilter: fileFilter
});

In the multer.js file, we start by setting up a folder where all the uploaded music files will be uploaded. We need to make this file static by defining that in the index.js file:

app.use(‘/uploads’, express.static(‘uploads’));

After that, we write a simple validator that will check the file mimetype before uploading. We then define the multer instance by adding the storage location, the limits of each file, and the validator that we created.

Create The Necessary Routes

Now let’s create our routes. Below is the list of endpoints we will be creating.

HTTP POST /music
Add new music

HTTP GET /music
Get all music

HTTP DELETE /music/:blogId
Delete a music

Let’s start by creating the blog route. Head over to api/routes/music.js and write the following code:

const express = require(“express”);
const router = express.Router();
const musicController = require(“../controller/musicController”);
const upload = require(“../../config/multer”);
router.get(“/”, musicController.getAllMusics);
router.post(“/”, upload.upload.single(“music”), musicController.addNewMusic);
router.delete(“/:musicId”, musicController.deleteMusic);
module.exports = router;

Note: Now whenever we make a get request to /music. the route calls the getAllMusic function that is located in the ‘controllers’ file.

Let’s move on over to api/controllers/musicController to define the controllers. We start by writing a function to get all the music in our database using the mongoose db.collection.find method which will return all the items in that collection.

After doing that, we write another function that will create a piece of new music in the database. We need to create a new music instance using the new keyword and then define the music object. After doing this, we will use the mongoose save method to add new music to the database.

In order to delete a piece of music, we need to use the mongoose remove method by simply passing the music ID as a parameter in the remove instance. This results to mongoose looking into the music collection that has that particular ID and then removing it from that collection.

let mongoose = require(“mongoose”);
const Music = require(“../model/Music”);
exports.getAllMusics = async (req, res) => {
try {
let music = await Music.find();
res.status(200).json(music);
} catch (err) {
res.status(500).json(err);
}
};
exports.addNewMusic = async (req, res) => {
try {
const music = new Music({
title:req.body.title,
artist:req.body.artist,
music:req.file
});

let newMusic = await music.save();
res.status(200).json({ data: newMusic });
} catch (err) {
res.status(500).json({ error: err });
}
};
exports.deleteMusic = async (req, res) => {
try {
const id = req.params.musicId;
let result = await Music.remove({ _id: id });
res.status(200).json(result);
} catch (err) {
res.status(500).json(err);
}
};

Last but not least, in order to test the routes, we need to register the music routes in our index.js file:

const userRoutes = require(“./api/user/route/user”); //bring in our user routes
app.use(“/user”, userRoutes);

Testing The End Points

To test our endpoints, we will be using POSTMAN.

Adding New Music

To test the Add Music functionality, set the method of the request by clicking on the methods drop-down. After doing this, type the URL of the endpoint and then click on the body tab to select how you want to send your data. (In our case, we will be using the form-data method.)

So click on the form-data and set up your model key. As you set it up, give the keys some value as shown in the image below:

Testing Adding new music API using Postman

Testing Adding new music API in Postman dashboard (Large preview)

After doing this, click on ‘Send’ to make the request.

Listing All Music

To list all of the music in our database, we have to type the endpoint URL in the URL section provided. After doing this, click on the ‘Send’ button to make the request.

Testing Listing API using Postman

Testing Listing API in Postman dashboard (Large preview)

Deleting Music

To delete a piece of music, we need to pass the music id as a parameter.

Testing Delete API using Postman

Testing Delete API Postman dashboard (Large preview)

That’s it!

Building The Frontend

For our frontend, we will be using a Vue framework: Nuxt.js.

“Nuxt is a progressive framework based on Vue.js to create modern web applications. It is based on Vue.js official libraries (vue, vue-router and vuex) and powerful development tools (webpack, Babel and PostCSS).”

— NuxtJS Guide

To create a new Nuxt.js application, open up your terminal and type in the following (with musicapp as the name of the app we will be building):

$ npx create-nuxt-app musicapp

During the installation process, we will be asked some questions regarding the project setup:

Project name
musicapp

project description
A Simple music manager app

Author name
<your name>

Package manager
npm

UI framework
Bootstrap vue

custom ui framework
none

Nuxt modules
Axios,pwa (use the spacebar on your keyboard to select items)

Linting tool
Prettier

test framework
None

Rendering Mode
Universal (SSR)

development tool
Jsonconfig.json

After selecting all of this, we have to wait a little while for the project to be set up. Once it’s ready, move into the /project folder and serve the project as follows:

cd musicapp && npm run dev

Open up the project in any code editor of your choice and then open the project in the browser by accessing localhost:3000.

Preview Of Nuxt.js project

Nuxt.js Project Preview (Large preview)

Configuring Axios

We will be using axios to make an HTTP request to our back-end server. Axios is already installed in our project, so we just have to configure the baseURL- to our backend server.

To do this, open the nuxt.config.js file in the root directory and add the baseURL in the axios object.

axios: {
baseURL:’http://localhost:4000′
},

Building The Music Manager

Setting Up The UI

Let’s start by cleaning up the UI. Open up the pages/index.vue file and remove all of the code in there with the following:

<template>
<div>Hello</div>
</template>

After doing this, you should only be able to see a “Hello” in the browser.

In the root directory, create a /partials folder. Inside the /partials folder, create a navbar.vue file and add the following code:

<template>
<header>
<nav class=”navbar navbar-expand-lg navbar-light bg-info”>
<div class=”container”>
<a class=”navbar-brand” href=”#”>Music App</a>
<button
class=”navbar-toggler”
type=”button”
data-toggle=”collapse”
data-target=”#navbarNav”
aria-controls=”navbarNav”
aria-expanded=”false”
aria-label=”Toggle navigation”
>
<span class=”navbar-toggler-icon”></span>
</button>
<div class=”collapse navbar-collapse justify-content-end” id=”navbarNav”>
<ul class=”navbar-nav”>
<li class=”nav-item active”>
<a class=”nav-link” href=”#”>Player</a>
</li>
<li class=”nav-item”>
<a class=”nav-link” href=”#”>Manager</a>
</li>
</ul>
</div>
</div>
</nav>
</header>
</template>
<style scoped>
.nav-link,
.navbar-brand {
color: #ffff !important;
}
</style>

Note: We will be using the component to navigate through pages in our application. This is just going to be a simple component made up of Bootstrap navbar. Check out the official Bootstrap documentation for more reference.

Next, let’s define a custom layout for the application. Open the /layouts folder, replace the code in the default.vue file with the code below.

<template>
<div>
<navbar />
<nuxt />
</div>
</template>
<script>
import navbar from ‘@/partial/navbar’
export default {
components: {
navbar
}
}
</script>

We import the navbar into this layout, meaning that all the pages in our application will have that navbar component in it. (This is going to be the component that all other component in our application will be mounted.)

After this, you should be able to see this in your browser:

Nuxt.js Navbar component after modification

Nuxt.js Navbar component (Large preview)

Now let’s setup the UI for our manager. To do this, we need to create a /manager folder within the components folder and then add a file into the folder named manager.vue.

In this file, add the following code:

<template>
<section class=”mt-5″>
<div class=”container mb-4″>
<div class=”row”>
<div class=”col-md-12″>
<div class=”card”>
<div class=”card-body”>
<div class=”card-title mb-4″>
<h4>Add Music</h4>
</div>
<form>
<div class=”form-group”>
<label for=”title”>Title</label>
<input type=”text” class=”form-control” />
</div>
<div class=”form-group”>
<label for=”artist”>Artist</label>
<input type=”text” class=”form-control” />
</div>
<div class=”form-group”>
<label for=”artist”>Music</label>
<div class=”custom-file”>
<input type=”file” class=”custom-file-input” id=”customFile” />
<label class=”custom-file-label” for=”customFile”>Choose file</label>
</div>
</div>
<div class=”form-group”>
<button class=”btn btn-primary”>Submit</button>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
<div class=”container”>
<div class=”row”>
<div class=”col-md-12″>
<div class=”card bg-light p-1 showdow-sm”>
<div class=”card-title”>
<button class=”btn btn-info m-3″>Add Music</button>
</div>
<div class=”card-body”>
<table class=”table”>
<thead>
<tr>
<th scope=”col”>#</th>
<th scope=”col”>Title</th>
<th scope=”col”>Artist</th>
<th scope=”col”>Date created</th>
<th scope=”col”>Action</th>
</tr>
</thead>
<tbody>
<tr>
<td>1</td>
<td>Demo Title</td>
<td>Wisdom.vue</td>
<td>12/23/13</td>
<td>
<button class=”btn btn-info”>Delete</button>
</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
</section>
</template>

Note: This is just a simple Bootstrap template for adding music into our application. The form will define a table template that will list all os the music that can be found in our database.

After defining this component, we need to register it in the /pages folder to initailize routing.

Nuxt.js doesn’t have a ‘router.js’ file like Vue.js. It uses the pages folder for routing. For more details, visit the Nuxt.js website.

To register the component, create a /manager folder within the /pages folder and create an index.vue file. Then, place the following code inside the file:

<template>
<div>
<manager />
</div>
</template>
<script>
import manager from ‘@/components/manager/manager’
export default {
components: {
manager
}
}
</script>

This is the component that will render in our pages route.

After doing this, head over to your browser and navigate to /manager — you should be seeing this:

UI of music Manager

Music manager UI (Large preview)

Listing All Music

Let’s continue by creating a function that will fetch all of the music. This function will be registered in the created life cycle hook, so that whenever the component is created, the function will be called.

Let’s start by creating a variable in the vue instance that will hold all of the music:

allmusic = [];
musicLoading: false,

Then, define a getAllMusics function and add the following code:

async getAllMusics() {
this.musicLoading = true
try {
let data = await this.$axios.$get(‘/music’)
this.allmusic = data
this.musicLoading = false
} catch (err) {
this.musicLoading = false
swal(‘Error’, ‘Error Fetting Musics’, ‘error’)
}
}

Next, register within the created life cycle hook:

created() {
this.getAllMusics()
}

Outputting The Data

Now it’s time to output all of the songs on the table which we’ve created earlier:

<table class=”table”>
<thead>
<tr>
<th scope=”col”>#</th>
<th scope=”col”>Title</th>
<th scope=”col”>Artist</th>
<th scope=”col”>Date created</th>
<th scope=”col”>Action</th>
</tr>
</thead>
<div
v-if=”musicLoading”
class=”spinner-border”
style=”width: 3rem; height: 3rem;”
role=”status”
>
<span class=”sr-only”>Loading…</span>
</div>
<tbody v-else>
<tr v-for=”(music, index) in allmusic” :key=”index”>
<td>{{ index + 1 }}</td>
<td>{{ music.title }}</td>
<td>{{ music.artist }}</td>
<td>{{ music.created }}</td>
<td>
<button class=”btn btn-info” @click=”deleteMusic(music._id)”>Delete</button>
</td>
</tr>
</tbody>
</table>

Remember that table we created earlier? Well, we will need to loop through the response we get back from our backend to list all of the music received back from the database.

Adding Music

To add a new piece of music we need to make an HTTP request to the back-end server with the music details. To do this, let’s start by modifying the form and handling of the file uploads.

On the form, we need to add an event listener that will listen to the form when it is submitted. On the input field, we add a v- model to bind the value to the input field.

<form @submit.prevent=”addNewMusic”>
<div class=”form-group”>
<label for=”title”>Title</label>
<input type=”text” v-model=”musicDetails.title” class=”form-control” />
</div>
<div class=”form-group”>
<label for=”artist”>Artist</label>
<input type=”text” v-model=”musicDetails.artist” class=”form-control” />
</div>
<div class=”form-group”>
<label for=”artist”>Music</label>
<div class=”custom-file”>
<input
type=”file”
id=”customFile”
ref=”file”
v-on:change=”handleFileUpload()”
class=”custom-file-input”
/>
<label class=”custom-file-label” for=”customFile”>Choose file</label>
</div>
</div>
<div class=”form-group”>
<button class=”btn btn-primary” :disabled=”isDisabled”>
<span
class=”spinner-border spinner-border-sm”
v-if=”addLoading”
role=”status”
aria-hidden=”true”
></span>Submit
</button>
</div>
</form>

And the script section should look like this:

<script>
export default {
data() {
return {
musicDetails: {
title: ”,
artist: ”,
music: ”
},
allmusic = [],
musicLoading: false,
isValid: false;
addLoading: false,
}
},
computed: {
isDisabled: function() {
if (
this.musicDetails.title === ” ||
this.musicDetails.artist === ” ||
this.musicDetails.music === ”
) {
return !this.isValid
}
}
},
methods: {
handleFileUpload() {
this.musicDetails.music = this.$refs.file.files[0]
console.log(this.musicDetails.music.type)
},
addNewMusic() {
let types = /(.|/)(mp3|mp4)$/i
if (
types.test(this.musicDetails.music.type) ||
types.test(this.musicDetails.music.name)
) {
console.log(‘erjkb’)
} else {
alert(‘Invalid file type’)
return !this.isValid
}
}
}
}
</script>

We will define a function that will send a request to our back-end service to create any new music that has been added to the list. Also. we need to write a simple validation function that will check for the file type so that the users can only upload files with an extention of .mp3 and .mp4.

It’s important to define a computed property to make sure that our input field isn’t empty. We also need to add a simple validator that will make sure the the file we are trying to upload is actually a music file.

Let’s continue by editing the addMusic function to make a request to our back-end service. But before we do this, let’s first install sweetalert which will provide us with a nice modal window. To do this this, open up your terminal and type in the following:

npm i sweetalert

After installing the package, create a sweetalert.js file in the /plugins folder and add this:

import Vue from ‘vue’;
import swal from ‘sweetalert’;

Vue.prototype.$swal = swal;

Then, register the plugin in the nuxt.config.js file inside the plugin instace like this:

plugins: [
{
src: ‘~/plugins/sweetalert’
}
],

We have now successfully configured sweetalert in our application, so we can move on and edit the addmusic function to this:

addNewMusic() {
let types = /(.|/)(mp3|mp4)$/i
if (
types.test(this.musicDetails.music.type) ||
types.test(this.musicDetails.music.name)
) {
let formData = new FormData()
formData.append(‘title’, this.musicDetails.title)
formData.append(‘artist’, this.musicDetails.artist)
formData.append(‘music’, this.musicDetails.music)
this.addLoading = true
this.$axios
.$post(‘/music’, formData)
.then(response => {
console.log(response)
this.addLoading = false
this.musicDetails = {}
this.getAllMusics() // we will create this function later
swal(‘Success’, ‘New Music Added’, ‘success’)
})
.catch(err => {
this.addLoading = false
swal(‘Error’, ‘Something Went wrong’, ‘error’)
console.log(err)
})
} else {
swal(‘Error’, ‘Invalid file type’, ‘error’)
return !this.isValid
}
},

Let’s write a simple script that will toggle the form, i.e it should only display when we want to add new music.

We can do this by editing the ‘Add Music’ button in the table that displays all of the music that can be found:

<button
class=”btn btn-info m-3″
@click=”initForm”>
{{addState?”Cancel”:”Add New Music”}}
</button>

Then, add a state that will hold the state of the form in the data property:

addState: false

After doing this, let’s define the initForm function:

initForm() {
this.addState = !this.addState
},

And then add v-if="addState" to the div that holds the form:

<div class=”card” v-if=”addState”>

Deleting Music

To delete music, we need to call the delete endpoint and pass the music id as a param. Let’s add a click event to the ‘Delete’ button that will trigger the function to delete a function:

<button class=”btn btn-info” @click=”deleteMusic(music._id)”>Delete</button>

The delete function will be making an HTTP request to our back-end service. After getting the music ID from the deleteMusic function parameter, we will add the ID in the URL that we are using to send the request. This specifies the exact piece of music that ought to be removed from the database.

deleteMusic(id) {
swal({
title: ‘Are you sure?’,
text: ‘Once deleted, you will not be able to recover this Music!’,
icon: ‘warning’,
buttons: true,
dangerMode: true
}).then(willDelete => {
if (willDelete) {
this.$axios
.$delete(‘/music/’ + id)
.then(response => {
this.getAllMusics()
swal(‘Poof! Your Music file has been deleted!’, {
icon: ‘success’
})
})
.catch(err => {
swal(‘Error’, ‘Somethimg went wrong’, ‘error’)
})
} else {
swal(‘Your Music file is safe!’)
}
})
}

With all of this, we have just built our music manager. Now it’s time to build the music player.

Let’s start by creating a new folder in the components folder named /player. Then, create a player.vue file within this folder and add this:

<template>
<section>
<div class=”container”>
<div class=”row”>
<div class=”col-md-12″>
<h3 class=”text-center”>Player</h3>
</div>
</div>
</div>
</section>
</template>
<script>
export default {
data() {
return {}
}
}
</script>
<style scoped>
</style>

Next, let’s import this component into the index.vue file in the /pages folder. Replace the code in index.vue file to this:

<template>
<div>
<player />
</div>
</template>
<script>
import player from ‘@/components/player/player’
export default {
components: {
player
}
}
</script>

Let’s configure routing in our navbar component to enable routing between our pages.

To route in a Nuxt.js application, the nuxt-link is used after which you have specified the page for that route to a particular instance. So let’s edit the code in the partials/navbar component to this:

<template>
<header>
<nav class=”navbar navbar-expand-lg navbar-light bg-info”>
<div class=”container”>
<nuxt-link to=”/” class=”navbar-brand”>Music App</nuxt-link>
<button
class=”navbar-toggler”
type=”button”
data-toggle=”collapse”
data-target=”#navbarNav”
aria-controls=”navbarNav”
aria-expanded=”false”
aria-label=”Toggle navigation”
>
<span class=”navbar-toggler-icon”></span>
</button>
<div class=”collapse navbar-collapse justify-content-end” id=”navbarNav”>
<ul class=”navbar-nav”>
<li class=”nav-item active”>
<nuxt-link to=”/” class=”nav-link”>Player</nuxt-link>
</li>
<li class=”nav-item”>
<nuxt-link to=”/manager” class=”nav-link”>Manager</nuxt-link>
</li>
</ul>
</div>
</div>
</nav>
</header>
</template>
<style scoped>
.nav-link,
.navbar-brand {
color: #ffff !important;
}
</style>

With this, we can navigate through our pages by using the navbar.

Building The Player

Before we begin, we need to extend Webpack to load audio files. Audio files should be processed by file-loader. This loader is already included in the default Webpack configuration, but it is not set up to handle audio files.

To do this, go to the nuxt.config.js file and modify the build object to this:

build: {
extend(config, ctx) {
config.module.rules.push({
test: /.(ogg|mp3|mp4|wav|mpe?g)$/i,
loader: ‘file-loader’,
options: {
name: ‘[path][name].[ext]’
}
})
}
}

Next, let’s write a function that will get all songs and then use the Audio constructor to play the first song in the allMusic array.

For starters, let’s modify our player.vue file to this:

<template>
<section v-if=”allMusic”>
<div class=”container”>
<div class=”row”>
<div class=”col-md-12″>
<h3 class=”text-center”>Player</h3>
</div>
</div>
<div class=”row”>
<div class=”col-md-6″>
<span>{{this.current.title}} – {{this.current.artist}}</span>
</div>
</div>
</div>
</section>
</template>
<script>
export default {
data() {
return {
current: {
title: ”,
artist: ”
},
song: true,
isplaying: false,
allMusic: null,
index: 0,
player: ”
}
},
methods: {
async initPlayer() {
if (this.allMusic !== []) {
this.current = await this.allMusic[this.index]
this.player.src = `http://localhost:4000/${this.current.music.path}`
} else {
this.song = true
}
},
async getAllSongs() {
try {
let response = await this.$axios.$get(‘/music’)
console.log(response)
if (response === []) {
this.song = true
this.current = null
} else {
this.song = false
this.allMusic = response
}
await this.initPlayer()
} catch (err) {
this.current = null
console.log(err)
}
}
},
created() {
if (process.client) {
this.player = new Audio()
}
this.getAllSongs()
}
}
</script>
<style scoped>
</style>

Once the file is served, the music will play in the background and then you should be able to see this in your browser:

UI of Music player

Music player UI (Large preview)

To stop the music, all you need to do is comment out the await player.play() in the initPlayer function.

Creating The Player UI

Let’s now define our music player UI by replacing the template in our player.vue file with the following:

<template>
<section v-if=”allMusic”>
<div class=”container”>
<div class=”row mb-5″>
<div class=”col-md-12″>
<h3 class=”text-center”>Player</h3>
</div>
</div>
<div class=”row mt-5″>
<div class=”col-md-6″>
<img
src=”https://images.pexels.com/photos/3624281/pexels-photo-3624281.jpeg?auto=compress&cs=tinysrgb&dpr=1&w=500″
class=”image”
/>
<div class=”card player_card”>
<div class=”card-body”>
<h6 class=”card-title”>
<b>{{this.current.title}} – {{this.current.artist}}</b>
</h6>
<div>
<i class=”fas fa-backward control mr-4″></i>
<i class=”fas fa-play play”></i>
<i class=”fas fa-pause play”></i>
<i class=”fas fa-forward control ml-4″></i>
</div>
</div>
</div>
</div>
<div class=”col-md-6″>
<div class=”card shadow”>
<table class=”table”>
<thead>
<tr>
<th scope=”col”>#</th>
<th scope=”col”>Title</th>
<th scope=”col”>Artist</th>
<th scope=”col”>Action</th>
</tr>
</thead>
<tbody>
<tr>
<th scope=”row”>1</th>
<td>Mark</td>
<td>Otto</td>
<td>
<button class=”btn btn-primary”>Play</button>
</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</div>
</section>
</template>

Then, add the following style into the style section:

<style scoped>
.image {
border-radius: 5px !important;
position: relative;
height: 300px;
width: 100%;
}
.player_card {
text-align: center;
bottom: 20px;
margin: 0px 40px;
}
.text-muted {
font-size: 15px;
}
.play {
font-size: 40px;
}
.control {
font-size: 25px;
}
</style>

After modifying this, the player should look like this:

Music player final UI

Final UI of music player (Large preview)

Adding The Play Function

We’ll continue by displaying the music description on the table. In order to do this, replace the table with the code below:

<table class=”table”>
<thead>
<tr>
<th scope=”col”>#</th>
<th scope=”col”>Title</th>
<th scope=”col”>Artist</th>
<th scope=”col”>Action</th>
</tr>
</thead>
<tbody>
<tr v-for=”(music,index) in allMusic” :key=”index”>
<th scope=”row”>{{index+1}}</th>
<td>{{music.title}}</td>
<td>{{music.artist}}</td>
<td>
<button class=”btn btn-primary”>Play</button>
</td>
</tr>
</tbody>
</table>

We don’t want to display the ‘Play’ and ‘Pause’ icons at the same time. Instead, we want a situation that when the song is playing, the ‘Pause’ icon is displayed. Also, when the song is paused, the play icon should be displayed.

To achieve this, we need to set a isPlaying state to the false instance and then use this instance to toggle the icons. After that, we will add a function to our ‘Play’ icon.

isplaying:false

After doing this, modify your ‘Play’ and ‘Pause’ icon to this:

<i class=”fas fa-play play” v-if=”!isplaying” @click=”play”></i>
<i class=”fas fa-pause play” v-else></i>

With all this let’s define the play method:

play(song) {
console.log(song)
if (song) {
this.current = song
this.player.src = `http://localhost:4000/${this.current.music.path}`
}
this.player.play()
this.isplaying = true
},

We, first of all, get the current song and pass it into the function parameter. We then define the JavaScript Audio() instance. Next, we check if the song is null: If it isn’t, we set this.current to the song we passed in the parameter, and then we call the Audio player instance. (Also, don’t forget that we have to set the isPlaying state to true when the music is playing.)

Adding The Pause Function

To pause a song, we will use the Audio pause method. We need to add a click event to the pause icon:

<i class=”fas fa-pause play” @click=”pause” v-else></i>

And then define the function in the methods instance:

pause() {
this.player.pause()
this.isplaying = false
},

Playing A Song From The Music List

This is quite simple to implement. All we have to do is add a click event that will change the song parameter in the play method to the song we just created.

Simply modify the play button on the music list table to this:

<button class=”btn btn-primary” @click=”play(music)”>Play</button>

And there you have it!

Adding The Next Function

To add the next function, we need to increment the index by one. To do this, add a click event to the next icon:

@click=”next”

And then define the prev function in the methods instance:

next() {
this.index++
if (this.index > this.allMusic.length – 1) {
this.index = 0
}
this.current = this.allMusic[this.index]
this.play(this.current)
},

This conditional is responsible for replaying all of the songs whenever the last song in the list has been played.

Adding The previous Function

This is actually the opposite of the next function, so let’s add a click event to the previous function:

@click=”prev”

Next, we define the previous function:

prev() {
this.index–
if (this.index < 0) {
this.index = this.allMusic.length – 1
}
this.current = this.allMusic[this.index]
this.play(this.current)
},

Our music player app is now complete!

Conclusion

In this article, we looked at how we can build a music manager with Nuxt.js and Express.js. Along the way, we saw how Multer streamlines the process of handling file uploads and how to use Mongoose to interact without a database. Finally, we used Nuxt.js to build the client app which gives it a fast and snappy feel.

Unlike other frameworks, building an application with Nuxt.js and Express.js is quite easy and fast. The cool part about Nuxt.js is the way it manages your routes and makes you structure your apps better.

You can access more information about Nuxt.js here.
You can access the source code on Github here

Smashing Editorial
(dm, il)

Adobe MAX Creativity Tour: Catch up on all the action

Original Source: http://feedproxy.google.com/~r/CreativeBloq/~3/d8ohOhx-ch4/adobe-max-creativity-tour-catch-up-on-all-the-action

Adobe MAX Creativity Tour London was packed with inspiring stories, jaw-dropping demos and fascinating creative insights. And if you missed out on the main event, you can catch up on all the talks from the comfort of your own home (or desk) right here. It's ideal for a shot of inspiration and motivation in these dingy winter days. Whether you're a designer, illustrator, blogger, photographer or budding student creative, there's something here for you.

The evening kicked off with a fascinating and useful insight into the tools you're almost certainly using to bring your projects to life: Adobe Creative Cloud. There have been several headline-grabbing updates to flagship tools, as well as exciting additions to the CC family, but you might not be quite clued up on all the changes. 

Principal Creative Cloud evangelist Rufus Deuchler offered a speedy tour of the most exciting updates, from the Sensei-powered brushes in new art app Adobe Fresco, to XD's powerful new co-editing capabilities, to Photoshop's downright amazing Object Selection tool. Get up to speed below.

The second talk shone a light on the incredible showcase of talent that was the McDonald's 2019 Christmas advert. Designers from Leo Burnett and Passion Animation Studios took to the stage to reveal how they came up with a concept that would capture the nation's hearts, and share how they brought it all to life. Catch up on their presentation below.

Rounding off the evening in style, Jamal Edwards MBE joined Adobe's Claire Darley on stage to chat about how he made the journey from getting his first camera aged 15 to curating stages at Wireless and recording interviews at 10 Downing Street. It makes for incredibly inspiring viewing – it's worth tuning in to hear the section on Edwards' hilariously cheeky guerilla marketing tactics alone [2 mins 50].

For more insight and inspiration, plus information on those Adobe Creative Cloud updates, head to the Adobe website.

Adobe MAX is heading to Europe

There's more exciting news for designers who can't make it across the pond for Adobe's annual US bash – in 2020, MAX will be coming to Europe for the first time! 

Adobe MAX Europe will take place 15-16 June 2020 at the Feira Internacional in Lisbon, Portugal. The festival will bring together the world’s biggest brands and most inspiring minds to celebrate Adobe’s vision of ‘Creativity for All’ – as well as, of course, revealing the latest innovations coming to Creative Cloud. 

To register, head to the Adobe MAX Europe site. Don't hang about though – there's a special launch discount that means if you pick up your ticket before the end of February, you'll pay less than half price!


Branding and Visual Identity for Bestside Films

Original Source: http://feedproxy.google.com/~r/abduzeedo/~3/2zxv3kg8Ffo/branding-and-visual-identity-bestside-films

Branding and Visual Identity for Bestside Films
Branding and Visual Identity for Bestside Films

abduzeedoFeb 26, 2020

Massimo Studio shared an awesome branding and visual identity project for Bestside Films.  Bestside’s main goal is to make history. They believe in the core pillars upon brands are created: Relationship, Movement and History. Always seeking to understand what is the needs of each project, aligning technique, sensitivity, and creativity. We will make history!

For the brand identity, the amazing people at Massimo Studio explored Bestside’s versatility to approach any kind of film project to create strong key visuals and a dynamic logo which can transform itself to attend the type of work they’re playing at the time. The bold applications relate to the ambitions the company has in their scope and with the purpose of no one passing to some piece of Bestside’s work without noticing them.

It’s dynamic logo which can transform itself depending on the client

Design Services: Brand Identity; Key Visuals; Goods
Sector: Film; Audio-visual
Website: bestside.com.br
Branding & Visual Identity


Animating SVG Text on a Path

Original Source: http://feedproxy.google.com/~r/tympanus/~3/TgvvW-zgNkY/

Animating SVG text on a path on scroll has been explained really well in this great video tutorial by the keyframers. The basic idea is to couple the startOffset value of a textPath element with the scroll position, allowing the text to move along its path while scrolling.

We wanted to take this a step further and integrate it in a real website example with some more features.

In our experiment, we made the animation smoother and used SVG filters, while also using different paths. Additionally, we worked with the Intersection Observer API for animating only the texts that are in the viewport. The intensity of the SVG filters depends on the scroll speed.

If you want to learn more about SVG filters and how to use them to create interesting effects, have a look at our dedicated series written by Sara Soueidan:

SVG Filters 101Outline Text with <feMorphology>Poster Image Effect with <feComponentTransfer>Duotone Images with <feComponentTransfer>Conforming Text to Surface Texture with <feDisplacementMap>Creating Texture with <feTurbulence>SVG Filter Effects: Moving Forward

Please be aware that animating SVG filters in Firefox has dreadful performance. This has been like that for years, unfortunately. There are a number of bugs filed regarding animating SVG filters and even rendering SVG filters:

Bug 1456932: SVG blur filter is slowBug 422371: SVG feTurbulence filter is far too slowBug 1583828: Slow SVG filter animationBug 483868: Firefox much less responsive than Chrome while scrolling/enlarging SVG map with filters

Sadly, the outlook on solving these issues in Firefox don’t look too good as these have all been marked with priority P3, which means:

“This isn’t a bad idea, and maybe we’ll want to implement it at some point in the future, but it’s not near-term roadmap material. Some core Bugzilla developer may work on it.”

So it’s a good idea to keep in mind that if you are working with SVG filters and plan to animate them, it’s probably best if you leave Firefox out of the equation. This is exactly what we did in our example, so you won’t see the fancy filter magic if you open the demo in Firefox. However, if you do want to try it out, you can do so with smaller areas, i.e. smaller texts. Although it won’t be as smooth as in Chrome, it will work better than with larger texts.

We hope you enjoy our examples and find them useful!

References & Credits

Animate Text on Scroll | SVG textPath Tutorial | Keyssentials: Quick Tips by @keyframersMoving Text on a Curved Path on CSS-TricksPerfecting Paths for <textPath> by Amelia Bellamy-RoydsImages from UnsplashimagesLoaded by Dave DeSandro

Animating SVG Text on a Path was written by Mary Lou and published on Codrops.

Motion Design Monday: Faceversary by Buck

Original Source: http://feedproxy.google.com/~r/abduzeedo/~3/MpkorMXaDHo/motion-design-monday-faceversary-buck

Motion Design Monday: Faceversary by Buck
Motion Design Monday: Faceversary by Buck

abduzeedoFeb 24, 2020

Yas NV and the incredible people at Buck keep delivering amazing work. What is a wonderful surprise for me is that I didn’t know much about Buck since a month ago. I guess I have been too busy with other things. To celebrate my admiration for Buck, here’s the motion design work they did for Facebook titled Faceversary. More information make sure to check out Buck’s website or visit Yas’ Behance profile.

Motion Design

Animation tests


Take your Adobe CC skills to the next level

Original Source: http://feedproxy.google.com/~r/CreativeBloq/~3/kBde5td-FAY/take-your-adobe-cc-skills-to-the-next-level

If you've been thinking about ways to continue your creative education this year, you've come to the right place. It's never too late to continue learning and finesse your personal and professional objectives. 

You can brush up on your design skills with the All-in-One Adobe Creative Cloud Suite Certification Bundle, now with 97% off. To extend your Adobe skills even further, you could check out our range of tutorials that include the best Photoshop tutorials, and Illustrator tutorials out there. 

All levels of expertise can benefit

With 60 hours of content and hundreds of tutorials, this updated Adobe CC bundle is the perfect way to get more out of the programs you already use or are interested in learning. The eight-course package is geared toward various levels of experience, so no matter what your expertise, you're bound to get something out of it and master your skills in Photoshop, Indesign, Illustrator, Lightroom, After Effects, and more. One of the best parts of the bundle is a certification upon completion, making it a valuable asset to add to your résumé and portfolio that will give you an edge over the competition.

Step your content up a gear

With endless amounts of lessons, you'll get the guidance you need to build your design portfolio with the latest updates in motion graphics, logo design, user interface design, photography, and so much more. This popular bundle is available 24/7, allowing you to come back and visit when you need to reference a specific tool or need a spark of inspiration for your latest project. Take your content to the next level, explore the latest updates on tool panels for each program, and even learn how to maximize your creative workflow to save you precious time. You'll be exposed to real-life exercises that test your understanding of the content, empowering you to practice what you learn.

While lifetime access to this bundle is priced at almost $2,000, The All-in-One Adobe Creative Cloud Suite Certification Bundle is currently price-dropped to only $34 - that's 97% off. It's certainly a great way to learn the latest and greatest in design artistry and continue to grow your career in 2020.

Read more:

The 5 best InDesign alternativesHow to add fonts in PhotoshopThe 6 best laptops for Photoshop in 2020

Audio-based Image Distortion Effects with WebGL

Original Source: http://feedproxy.google.com/~r/tympanus/~3/i6_NN9VRgWA/

We’ve covered in the past how we can read data from Audio, using the p5.sound library and how we can use that data, to draw things in the canvas, using p5.js.

Well, what if instead of drawing a sketch, we used audio to distort an image? Today we want to show you some demos that play around that idea.

We’ve created some experiments using the theme of movie trailers where the background image of the movie poster is being distorted using a sound sample. It kind of adds some drama to an otherwise static image in this case.

Here’s a short video of the beginning of one of the effects:

How it works

We analyze the sound and map the range of frequencies, to some uniforms we pass in our fragment shader. Then depending on the effect/distortion we have, we can tweak different parameters, using the audio frequencies which constantly change overtime.

In our first demo, we create a simple sinewave in our fragment shader, by using the bass frequencies of the audio track to control its frequency and the mid frequencies to control its amplitude. Then we add the distortion in both axes (x & y) of our uv and add that distortion to the initial texture coordinates.

It looks like this:

float wave = sin(uv.y * u_bass + u_time) * u_mid;
vec2 d = vec2(wave); // could be vec2(wave, 0.0) or vec2(0.0, wave) for distortion only in 1 axis.
vec4 image = texture2D(u_texture, uv + d);
gl_FragColor = image;

The possibilities are endless if you want to play around that idea, it’s just a matter of what effect you’re after. Make sure you’re mapping values to your uniforms, that are within a range that can distort your visual, and you can always use some generic uniforms like u_time, that can put some ‘overdrive’ to your distortion.

Head over to the demos and check out the variations we’ve made.

Hope you’ll have fun with this one and be sure to share any of your own versions!

Reference & Credits

p5.jsp5.soundp5jsShaderExamples

Audio-based Image Distortion Effects with WebGL was written by Yannis Yannakopoulos and published on Codrops.

3 Strategies to Follow When Your Site is Failing

Original Source: https://www.webdesignerdepot.com/2020/02/3-strategies-to-follow-when-your-site-is-failing/

Here are 3 strategies you should consider if your website is struggling. We’ll cover:

Web analytics and split testing
In-person testing
Simplifying your sales process

If possible, implement these strategies ahead of time before you run into any issues. I’m going to cover why these strategies are effective and what they are good at resolving.

1. Analytics and Split Testing

If you don’t know when or where your users are leaving, then you’re missing ou; this is extremely unhelpful if you are selling something.

Analytics will let you see the average amount of time spent per page, and which page your customers are exiting your website from.

If a user views your website and leaves after visiting your homepage without going any further, then you know exactly what to change. If they are spending an excessive amount of time navigating through simple parts of your sales process, then you know something may be wrong, and you should address it.

AB split testing…is particularly good at resolving weak points on your website where visitors are…changing their minds

Depending on which page of your website they are on, you may want them spending more or less time on it. An abnormally high amount of users abandoning their shopping cart might mean your checkout isn’t providing the user with a positive experience.

AB split testing refers to displaying different versions of the same page to different visitors. It is particularly good at resolving weak points on your website where visitors are leaving or changing their minds about going ahead with what you want them to (e.g. buying a product).

Let’s say we have 2000 page visitors, and 70% are leaving immediately from the landing page, and 600 are proceeding forward (30% click-through rate). Instead of presenting one landing page to all visitors, we display two landing pages and show one landing page to half the visitors, and the second landing page to the other half.

We make some changes to the original landing page and send some of the users to the new version and some to the original version. We do this to see if the new landing page will have a better click-through than the original, 30%.

A quick example for a skydiving company: Group A visitors see ‘Book’ in a smaller sized button, whereas Group B visitors see ‘Skydive NOW!’ in a larger sized button. If the ‘Skydive NOW!’ button improves the number of bookings the site receives, we could consider using Group B as the new control and further work on optimising the booking page, perhaps by creating a modification in which ‘Skydive NOW!’ has a different color scheme or is placed in a different position on the page.

This could be a minor re-design, such as changing the color of a button, to something more enticing, or a major re-design.

You can also display more than two versions of a page simultaneously, making multiple modifications, displaying three or more versions to different users. In this case, we would have the original version, A, and two or more modified pages B, C … so on. This can make split testing quicker by immediately testing multiple possibilities but it adds complexity to the process.

2. In-Person Testing

In-person, or remote testing, is a strategy in which you recruit a user to test your website by undertaking various tasks e.g., Buy a specific product and ship it to your home, or find some specific information on your website.

This should be done regularly during the web building process as by doing so when you launch your website, you might uncover major or recurring problems that could have been prevented. If you do it in early development stages, you can use the information from the test to plan before you start building things that don’t work!

If your website is already live and you overlooked testing beforehand, it’s not too late

If your website is already live and you overlooked testing beforehand, it’s not too late. You can still employ a handful of individuals to test it now.

In-person testing is typically done where you supervise an individual and instruct them to carry out certain tasks and take note of how they are navigating your website by seeing the way they browse the page and/or move their mouse. Ask them to think out loud. You should also ask them why they selected that option over another, what they liked about a particular feature, etc. 



If you already know which areas of your website need to be worked on, but you’re unsure of how to improve on it, you could ask your tester for specific insight.

I typically choose three users for testing, as it’s quick, easy, cheap, and manages to uncover lots of flaws I may have overlooked. Having more users testing the website can be beneficial, but typically most users end up pointing out the same weaknesses.

This is an effective strategy because having a handful of people test your website is like having someone read over your writing. You may not pick up on your own mistakes, but someone else will. It’s also important to note that the way (you), a web-designer browses the web is different from how the average person browses the web. You may have a perfect understanding of what is happening on your website because you created it. Still, someone who is using it for the first time won’t have the same knowledge and experience as you and will try to undertake tasks in the simplest, most intuitive way.

Remote testing is the same principle as in-person testing but executed remotely. This may save you the hassle of meeting up, but might mean that you need to use software (such as a camcorder) to monitor their browsing, along with voice or video calling, to discuss the process with them.

3. Simplifying Your Sales Process

Are you taking care of your customers and guiding them through their purchases?

What happens after your user lands on your website? Is there a good value proposition (product, service, or information that is appealing) compelling them to purchase?

Excellent, now you need to make sure the process is transparent and straightforward.

Be upfront about any extra fees or shipping costs. Let them know how long shipping may take. Customers want to feel like they can trust you.

Customers want to feel like they can trust you

Write out an FAQ (Frequently Asked Questions) addressing common concerns a potential customer may have. E.g., Let’s say I’m buying a dual SIM mobile (two sim cards, one mobile), I want to know which country the product is from, I want to know about the warranty, I may especially want to know details about the phone will store numbers from different cards.

Have an FAQ section addressing general sales questions and a product-specific FAQ. Along with that, address the product specifications and show high-quality photos or videos.

When the customer is satisfied with what they’ve selected, make the checkout process easy. Allow guest checkout if it’s a suitable option for your website. 

Show your customers you care about them and create reasons for them to want to share your website and products. Once you’ve made your sale, send them a follow-up email or little thank you. This will lead to more engagement and a repeat customer. Remember, selling a product isn’t the end goal. Making someone become a lifelong customer is, and you need to facilitate that.

 

Featured image via Unsplash.

Source

p img {display:inline-block; margin-right:10px;}
.alignleft {float:left;}
p.showcase {clear:both;}
body#browserfriendly p, body#podcast p, div#emailbody p{margin:0;}

How to Properly Organize Files in Your Codebase & Avoid Mayhem

Original Source: https://www.sitepoint.com/organize-project-files/?utm_source=rss

How to Properly Organize Files on a Project and Avoid Mayhem

The main library, data, UI, docs and wiki, tests, legacy and third-party components … How do we keep track and maintain order within all of this? Organizing the files in your codebase can become a daunting task.

Relax — we’ve got this! In this article, we’ll review the most common systems for both small and large projects, with some easy-to-follow best practices.

Why Bother?

As with pretty much all of the tasks related to project management — documentation, software commits, deployment — you’ll benefit from taking a conscious, programmatic approach. Not only it will reduce problems now, but it will also save you and your team quality time in the future when you need to quickly access and review things.

You surely can recall function names from the top of your head for whatever is it that you’re coding right now, and quickly find a file you need to edit, and sharply tell what works from what doesn’t — or so you think. But could you say the same about that project you were working on last year?

Let’s admit it: software projects can go on spans of inactivity that last for months, and even years. A simple README file could do a lot for your colleagues or your future self. But let’s think about the other ways you could structure your project, and establish some basic rules to name files, address project documentation, and to some degree organize an effective workflow that would stand the test of time.

Making Sense of Things

We’ll establish a “baseline” for organizing files in a project — a logic that will serve us for a number of situations within the scope of software development.

As with our rules for committing changes to your codebase the right way, none of this is carved in stone, and for what it’s worth, you and your team might come up with different guidelines. In any case, consistency is the name of the game. Be sure you understand (and discuss or dispute) what the rules are, and follow them once you’ve reached a consensus.

The Mandatory Set

This is a reference list of files that nearly every software project should have:

README: this is what GitHub renders for you right under the sourcetree, and it can go a long way to explaining what the project is about, how files are organized, and where to find further information.
CHANGELOG: to list what’s new, modified or discontinued on every version or revision — normally in a reverse chronological order for convenience (last changes first).
COPYING LICENSE: a file containing the full text of the license covering the software, including some additional copyright information, if necessary (such as third-party licenses).
.gitignore: assuming you use Git (you most probably do), this will also be a must to tell what files not to sync with the repository. (See Jump Start Git’s primer on .gitignore and the documentation for more info, and have a look at a collection of useful .gitignore templates for some ideas.)

Supporting Actors

The post How to Properly Organize Files in Your Codebase & Avoid Mayhem appeared first on SitePoint.

Collective #591

Original Source: http://feedproxy.google.com/~r/tympanus/~3/uZWpRlwKcss/

Collective item image

Inspirational Website of the Week: Six N. Five

A very interesting layout with beautiful transitions and great typography. Our pick this week.

Get inspired

Collective item image

Interactive Toys

A collection of cool interactive demos by Paul Neave.

Check it out

Collective item image

Styling The Good Ol’ Button Element

In this article, Ahmad Shadeed walks us through the fine details of a button element and how to style it to ensure that it looks good in all browsers.

Read it

Collective item image

Our Sponsor
Become a successful freelancer

Learn how to develop websites with the most popular WordPress theme in the world and secure your success as a freelancer.

Start learning

Collective item image

monica.css

Monica Dinculescu’s super-compact and useful CSS framework.

Check it out

Collective item image

Fixing memory leaks in web applications

Nolan Lawson shares his experience with fixing memory leaks in web applications, and provides some examples of how to effectively track them down.

Check it out

Collective item image

Art42

Art42.net showcases an infinite stream of unique AI art from the training of a carefully selected set of cubist art pieces. Based on StyleGAN2. Read some more about it in the HN comments.

Check it out

Collective item image

The Theory: A Semantic Color System

The first article in a series about how the YNAB team works with colors in their design system.

Read it

Collective item image

The Three Graces

Paul Henschel is testing out interactive low-key lighting in this amazing demo. Read more in his tweet and check out the source code.

Check it out

Collective item image

font-variant-numeric: tabular-nums

Sebastian De Deyne shares why he loves “font-variant-numeric: tabular-nums”.

Check it out

Collective item image

Don’t touch my clipboard

Alex Ellis writes about how you can (but shouldn’t) change how people copy text from your website.

Read it

Collective item image

CSS Tutorial: Create Diagonal Layouts Like It’s 2020

Nils Binder shows us how to create layouts with diagonal sections in a couple of steps.

Read it

Collective item image

Yelloworld

A beautiful web experience by Onetold Stories.

Check it out

Collective item image

BBC Micro bot

Send a tweet to @bbcmicrobot in BBC BASIC and it will run it on a 1980s 8-bit computer emulation and reply with a GIF.

Check it out

Collective item image

Let’s Define CSS 4

Read the interesting responses to the idea of officially defining “CSS 4”.

Read it

Collective item image

When CSS Blocks

Tim Kadlec shows why using an outdated preload/polyfill pattern is problematic.

Read it

Collective item image

Intimacy

A beautiful interactive poem composed with images, sounds and text.

Check it out

Collective item image

threejs-nuxt-sample

Misaki Nakano’s geometric Nuxt.js project. Find the source code here.

Check it out

Collective item image

macOS Catalina 10.15: Setting up a Brand New Mac for Development

Tania Rascia’s great guide on setting up a new Mac for development.

Read it

Collective item image

Svelte-grid

In case you didn’t stumble upon it yet: A draggable and resizable grid layout with responsive breakpoints, for Svelte.

Check it out

Collective item image

5 monospaced fonts with cool coding ligatures

Matej Latin explores some nice monospaced fonts suitable for coding.

Read it

Collective item image

YouTube Rewind 2019

Explore the best YouTube moments of 2019.

Check it out

Collective #591 was written by Pedro Botelho and published on Codrops.