Termly Acquires GDPR/CCPA Cookie Consent Banner, Turns Free Plugin Into a Commercial SaaS Product

Company A sells its plugin. Company B picks it up and moves forward with an overhauled version that looks and feels much different than the original. Users are outraged by the changes. It seems to be a repeating theme in 2021, almost as a rule rather than an exception.

Last month, Termly announced its acquisition of the GDPR/CCPA Cookie Consent Banner plugin. The plugin was a simple tool for adding and styling a consent banner for the front end. It is now a SaaS (Software as a Service) product that requires a Termly account to operate.

According to the team’s blog post, such changes were necessary. “Termly’s products, including the cookie consent management platform, are designed to cover the EU GDPR, the ePrivacy Directive, UK GDPR, and the CCPA. These laws require more than just a cookie consent banner to be compliant. Termly can help you build a privacy policy, create a Data Subject Access Request form, and comply with other privacy law requirements.”

In the past couple of weeks, users have taken to the WordPress.org review system, handing out 21 of the plugin’s 29 total one-star ratings. The project has over 200,000 users, so more should be expected if the general consensus is that this was a poor move by the company.

One of the complaints from users is the commercialization of the plugin. In the past, it was completely free to use. While there is still a free tier, users are limited to a mere 100 monthly unique visitors on a single domain. After hitting that limit, the banner will stop collecting consent records. The next level up costs $15 per month if paid annually.

Free and pro plans shown in the Termly pricing options for the Cookie Consent plugin.
New pricing options for the Termly service.

As Pattaya Web Services pointed out via Twitter, “GDPR/CCPA Cookie Consent Banner for #Wordpress has been purchased by #Termly and will now cost most website owners $180 per year.”

Termly must get a return on its investment. The company has developers to pay, and they have families to feed. But, I suspect the average user will not warm up to the so-limiting-that-it-is-free-in-name-only introduction level. Having to pay for features that have been free for years will not sit well with many.

Of course, there is always the option of using the old version, but Termly has no plans of maintaining it or ensuring that it meets compliance. The only alternative for small site owners who cannot afford to pay is to opt for another solution.

“I guess GDPR Cookie Consent banner, now operated by @Termly_io didn’t learn anything from [the] fiasco with WP User Avatar plugin reported by @wptavern earlier this year,” wrote user Gennady Kurushin on Twitter.

I believe they did. There are differences, and Termly’s handling of this showed a willingness to be transparent.

And, I cannot stress this enough: the new plugin is not an entirely different one unrelated to its core purpose. It was overhauled and turned into a SaaS product. At the end of the day, it is still a cookie consent management plugin — just different and costs a lot more for most users.

Unlike Dark Mode and ProfilePress, Termly did not make the changes in the dead of night. At least the company was upfront about everything. The team included an announcement in a point release two weeks before sending out the overhauled version. It disabled automatic updates so that users would not accidentally upgrade without being aware of what was coming. It even published a public blog post detailing what was happening.

Admin notice from the GDPR Cookie Consent Plugin before upgrading to 3.0.
Prior notice of upcoming changes in 3.0 and disabled auto-updates.

If anything, Termly took just about all the necessary steps it could have taken to prepare its user base. If a “right” way existed for a complete and utter makeover of a plugin, the company did as much.

That level of honesty is a bit more than we have seen in the past. The changes may still leave a bitter taste in the mouths of many users, but Termly should at least get a few points for making them in the light of day.

The result may be the same: fundamental changes in how the plugin operates, but users had a chance to ditch it or continue using the old version before anything went into effect. For some users, it may not be much, but that’s worth something.

I won’t be breaking out my pitchfork today, but I do not use the plugin. As more and more users upgrade to 3.0+ and realize they are essentially on the line for $180 per year, the reviews could get ugly.

Your Database Needs Unit Tests, and No Excuses

The test pyramid is a notable representation that helps to characterize programming tests. As you climb the means towards more noteworthy reconciliation of segments, you continue from many definite, quick, and disengaged tests towards less increasingly slow worldwide tests that approve the framework all in all. It bodes well on a fundamental level however it's harder to clarify how the separation between unit, joining, and start to finish tests should work. Conclusions contrast on which parts to coordinate at which layer in the pyramid. You would think the data set has a place in the upper layers since it is costly to set up and run. However, it likewise bodes well to coordinate it at the lower definite stage in the pyramid when it contains a business-basic rationale that requires itemized approval. It regularly needs the sort of thorough approval that you can't leave to a couple of moderate worldwide incorporation tests. Yet, we should begin with a recap of the meaning of a unit test. 

Speed, Single Focus, and Isolation 

In his phenomenal book Unit Testing Principles, Practices, and Patterns, Vladimir Khorikov's three necessities of a unit test are speed, single-center, and disconnection – if tests don't rely upon one another's state, they can be run in equal. Single-center can be found only: as a solitary unit of code versus a unit of conduct. The principal approach favors – indeed, requests – an exacting correspondence between a class under test and a test class, while the subsequent methodology centers around the public API and isn't essentially worried about approving execution subtleties. The unit of code approach expects you to remove the part of anything outer to the class under test by utilizing test copies (derides and nails). This surely applies to all connections with the information base. The unit of conduct approach is more sober-minded and possibly utilizes test pairs when utilizing the genuine article would debase execution. 

CSS Modules (The Native Ones)

They are actually called “CSS Module Scripts” and are a native browser feature, as opposed to the popular open-source project that essentially does scoped styles by creating unique class name identifiers in both HTML and CSS.

Native CSS Modules are a part of ES Modules (a lot like JSON modules we recently covered):

// Regular ES Modules
import React from "https://cdn.skypack.dev/react@17.0.1";

// Newfangled JSON Modules
import configData from './config-data.json' assert {type: 'json'};

// Newfangled CSS Modules
import styleSheet from "./styles.css" assert { type: "css" };

I first saw this from Justin’s tweet:

This is a Chrome-thing for now. Relevant links:

As I write, it only works in Chrome Canary with the Experimental Web Platform Features on. If your question is, When can I use this on production projects with a wide variety of users using whatever browser they want? I’d say: I have no idea. Probably years away. Maybe never. But it’s still interesting to check out. Maybe support will move fast. Maybe you’ll work on an Electron project or something where you can count on specific browser features.

This looks like an extension of Constructable Stylesheets, which are also Chrome-only, so browsers that are “behind” on this would have to start there.

I gave Justin’s idea a spin here:

If I log what I get back from the CSS Modules import, it’s a CSSStyleSheet:

Showing a styles tree with nodes for CSS rules, media, rules, and prototype.

If you’re going to actually use the styles… that’s on you. Justin’s idea basically applies the style as a one-liner because it just so happens that lit-html supports CSSStyleSheet (those docs don’t make that clear, but I imagine they will at some point). For native web components, it’s not much different: you import it, get the CSSStyleSheet, then apply it to the web component like:

this.shadowRoot.adoptedStyleSheets = [myCSSStyleSheet];

I would think the point of all this is:

  • we needed a way to import a stylesheet in JavaScript and this is basically the first really clear crack at it that I’m aware of,
  • now we have programatic access to manipulate the CSS before using it, if we wanted, and
  • it looks particularly nice for Web Components usage, but it’s generic. Do whatever you want with the stylesheet once you have it.

The post CSS Modules (The Native Ones) appeared first on CSS-Tricks. You can support CSS-Tricks by being an MVP Supporter.

Do You Really Need Kubernetes?

These days, it seems Kubernetes is a topic that is never too far from people's lips.  The tool, and the associated tools built around it, are talked about so often it seems it's the only subject important to developers these days - especially as the IT world becomes increasingly orientated towards cloud and microservices.

But in spite of all the conversation around Kubernetes... do you really need Kubernetes for your environment? Or is it just another case of the next 'new and shiny' object,  with people distracted by the novelty and possibility, rather than the facts? In this blog, I'll take a closer look at why Kubernetes might be a case of the hype outweighing the helpfulness in most cases. 

Scriptless Testing vs Scripted Testing: Which One Is for You?

Scriptless testing - a way of test automation that does not need scripting -  is a new method. It aims at minimizing a tester’s load in terms of learning to code and script! Just like in-sprint, scriptless testing is also a new and popular paradigm. If you have stumbled on this post, I am sure you must have heard about scriptless testing and are fascinated by its working. Or you are wondering whether you should go for scriptless or scripted testing? What’s in store for you if you choose one of them? Don’t worry. This post is just perfect to clear out your confusion. However, this post will not dive deep into scriptless testing and if you are interested in that solely, you may visit What is Scriptless testing and how it works.

Scripted Testing - Standard and Powerful Method

Scripted testing has been a standard method of automated testing for software and is today used in the majority of software development cycles. The following table shows the growth of the automation testing market over the years in billion dollars:

Simple Code: Acceptance Tests

Acceptance tests are a great tool to verify that the application or system works as expected from end to end. Sometimes these tests can be called end-to-end tests but sometimes end-to-end tests have a different meaning. Another term to describe the same functionality is QA tests and a subset of acceptance tests is often referred to as smoke tests.

The Idea

The idea is to define input and the expected output and once the system and all its dependant services are running the whole system can be verified to work as expected. In an ideal world, the acceptance tests would be implemented based on the acceptance criteria of the use case.

Low-Code Application Development and Its Importance for Businesses

From eCommerce to the Internet of Things, technology has driven change in almost every aspect of business — prompting owners and managers to remake, rebuild, and reconstruct the way they run their operations (Ismail).

While most companies feel reluctant to make the change (Beede et al), the rising popularity of low-code application development is paving opportunities for enterprises to digitize their operations, automate their workflows, and more (Gartner).

Usage of Practical Grep Commands Examples Useful in Real World Debugging in Linux

In our Daily debugging we need to analyze logs files of various products. Reading those log files is not an easy task, it requires special debugging skills which can only be gained through experience or by god’s grace. Now while debugging we might need to extract some of the data or we need to play with a log file which can not be done by just reading, there is a need for commands. 

There are many commands in Linux which are used by debuggers like grep, awk, sed, wc, taskset, ps, sort, uniq, cut, xargs, etc . . . 

configure oop settings on python-vlc tkinter script

I have been looking for a good way to embed vlc in a python tkinter frame.

I found a script on stackexchange that does the basic work, but - to embed the vlc in a frame - the script relies upon an oop technique I have asked about before but never been able to figure out.

In its original state the script didnt have any controls. It just played the video according to the default settings of the video. Consequently, the videos dimensions can open up huge, out of time, and one is unable to stop or pause playback, and even closing a video out is pain in the butt.

Anyway, I advanced the script by writing in some dimensions to play at a reasonable size, and I added in some tkinter buttons and defs where I want it to pause, resume, stop, etc... And I researched the code enough to find the correct bindings... I just dont get how to actually set the bindings and get the buttons commands to call them...

The script is below. The buttons are supposed to show on the bottom but rest a bit high at the top; and the root.destroy is problematic because it only closes tkinter, not the vlc player (so the video will keep playing). The video will also autoplay. But the main problem is getting it to pause and resume. The code is below, and the bindings (which are not in tkinter, but only vlc) are below the code.

The parameter in the Pause / Resume binding is set to 1 to play and 0 to stop, as the original article explains.

Any help much appreciated!

    import tkinter as tk
    import vlc

    class Screen(tk.Frame):

        '''
        Screen widget: Embedded video player from local or youtube
        '''

        def __init__(self, parent, *args, **kwargs):
            tk.Frame.__init__(self, parent, bg='black')
            self.parent = parent
            # Creating VLC player
            self.instance = vlc.Instance()
            self.player = self.instance.media_player_new()

        def GetHandle(self):
            # Getting frame ID
            return self.winfo_id()

        def play(self, _source):
            # Function to start player from given source
            Media = self.instance.media_new(_source)
            Media.get_mrl()
            self.player.set_media(Media)

            self.player.set_hwnd(self.winfo_id())
            self.player.play()         

    ########################################################################        

    def start (): pass

    def pause (): pass

    def resume (): pass

    def stop (): pass

    def close (): pass  

    ########################################################################        

    url = r"C:\Users\whoever\Desktop\whatever.mp4"    # changeable

    root = tk.Tk()
    root.geometry("600x600+100+100")
    player = Screen(root)
    player.place(x=0, y=0, width=600, height=400)
    player.play(url)

    framed2 = tk.Frame(root)  

    k = tk.Button(framed2, text='Play', command=start)
    k.grid(row=0,column=1)

    l = tk.Button(framed2, text='Pause', command=pause)
    l.grid(row=0,column=2)

    m = tk.Button(framed2, text='Stop', command=stop)
    m.grid(row=0,column=3)

    n = tk.Button(framed2, text='Quit', command=root.destroy)
    n.grid(row=0,column=5)

    framed2.pack(padx=5, pady=4)

    root.mainloop()

###############################################################
https://www.geeksforgeeks.org/python-vlc-medialistplayer-pause-resume/?ref=rp

import vlc
import time
media_player = vlc.MediaListPlayer()
player = vlc.Instance()
media_list = player.media_list_new()
media = player.media_new(url)
media_list.add_media(media)
media_player.set_media_list(media_list)
media_player.play()
time.sleep(5)
media_player.set_pause(1) #<---------------------------------- PAUSE / RESUME BINDING
time.sleep(4)

How to Code for Beginners

What Is Coding?

You must have heard about engineers doing coding in their systems, but what does it mean? It is an act of writing a language that can be understood by the computers, which let them perform a task. Codes are compiled and executed to let the process happen. We can create apps, software, websites with the help of coding. Codes are written in a different domain, for example, a machine learning engineer will write code for their ML models. A full-stack developer will write code to optimize their website, and an android developer will write code to make android applications. Coding can also be referred to like computer programming.

Why Should I Learn to Code?

Coding lets you decode your future easily. It opens new opportunities for the future. It not only improves your problem-solving skills but also helps in fulfilling your dream of getting the highest-paid jobs. 

10 Best WordPress Photo Plugins

Best WordPress Photo PluginsAre you a photographer looking to create a new WordPress website to showcase your best work? If that’s not the case, perhaps you’re a seasoned website owner looking to revamp an old photography website. Still, you can be a freelance web developer or an agency looking to create a photography website for your client. Or […]

The post 10 Best WordPress Photo Plugins appeared first on WPExplorer.

Implementing Circuit Breaker Pattern Using Spring Cloud Hystrix

Spring Cloud Hystrix is another important component of the Spring Cloud project. It is used to implement the Circuit Breaker pattern. The role of the circuit breaker is to enable fault tolerance in a microservice architecture in case some underlying service is down.

In other words, using a circuit breaker, we can fall back to a different path of the program automatically. Spring Cloud Hystrix helps make the switch seamless.

Top 10 June ’21 Big Data Articles to Read Now

Introduction

Big Data is now adapted by a lot of businesses. Its popularity and use are expanding globally. How awesome would it be to find top trending Big Data articles in one place so that you can always stay up to date with the latest trends in technology? We dug into Google analytics to find the top 10 most popular Big Data articles in June. Let's get started!

10. Kafka Administration and Monitoring UI Tools

Kafka is used for streaming data and much more! This article covers Kafka basics and Kafta Administration, Kafka Manager, and Monitoring tools. 

How to Code a Playable Synth Keyboard

With a little knowledge of music theory, we can use regular HTML, CSS and JavaScript — without any libraries or audio samples — to create a simple digital instrument. Let’s put that into practice and explore one method for creating a digital synth that can be played and hosted on the internet.

Here’s what we’re making:

We’ll use the AudioContext API to create our sounds digitally, without resorting to samples. But first, let’s work on the keyboard’s appearance.

The HTML structure

We’re going to support a standard western keyboard where every letter between A and ; corresponds to a playable natural note (the white keys), while the row above can be used for the sharps and flats (the black keys). This means our keyboard covers just over an octave, starting at C₃ and ending at E₄. (For anyone unfamiliar with musical notation, the subscript numbers indicate the octave.)

One useful thing we can do is store the note value in a custom note attribute so it’s easy to access in our JavaScript. I’ll print the letters of the computer keyboard, to help our users understand what to press.

<ul id="keyboard">
  <li note="C" class="white">A</li>
  <li note="C#" class="black">W</li>
  <li note="D" class="white offset">S</li>
  <li note="D#" class="black">E</li>
  <li note="E" class="white offset">D</li>
  <li note="F" class="white">F</li>
  <li note="F#" class="black">T</li>
  <li note="G" class="white offset">G</li>
  <li note="G#" class="black">Y</li>
  <li note="A" class="white offset">H</li>
  <li note="A#" class="black">U</li>
  <li note="B" class="white offset">J</li>
  <li note="C2" class="white">K</li>
  <li note="C#2" class="black">O</li>
  <li note="D2" class="white offset">L</li>
  <li note="D#2" class="black">P</li>
  <li note="E2" class="white offset">;</li>
</ul>

The CSS styling

We’ll begin our CSS with some boilerplate:

html {
  box-sizing: border-box;
}

*,
*:before,
*:after {
  box-sizing: inherit;
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen,
    Ubuntu, Cantarell, "Open Sans", "Helvetica Neue", sans-serif;
}

body {
  margin: 0;
}

Let’s specify CSS variables for some of the colors we’ll be using. Feel free to change them to whatever you prefer!

:root {
  --keyboard: hsl(300, 100%, 16%);
  --keyboard-shadow: hsla(19, 50%, 66%, 0.2);
  --keyboard-border: hsl(20, 91%, 5%);
  --black-10: hsla(0, 0%, 0%, 0.1);
  --black-20: hsla(0, 0%, 0%, 0.2);
  --black-30: hsla(0, 0%, 0%, 0.3);
  --black-50: hsla(0, 0%, 0%, 0.5);
  --black-60: hsla(0, 0%, 0%, 0.6);
  --white-20: hsla(0, 0%, 100%, 0.2);
  --white-50: hsla(0, 0%, 100%, 0.5);
  --white-80: hsla(0, 0%, 100%, 0.8);
}

In particular, changing the --keyboard and --keyboard-border variables will change the end result dramatically.

For styling the keys and the keyboard — especially in the pressed states — I owe a lot of my inspiration to this CodePen by zastrow. First, we specify the CSS shared by all the keys:

.white,
.black {
  position: relative;
  float: left;
  display: flex;
  justify-content: center;
  align-items: flex-end;
  padding: 0.5rem 0;
  user-select: none;
  cursor: pointer;
}

Using a specific border radius on the first and last key helps make the design look more organic. Without rounding, the top left and top right corners of keys look a little unnatural. Here’s a final design, minus any extra rounding on the first and last keys.

Let’s add some CSS to improve this.

#keyboard li:first-child {
  border-radius: 5px 0 5px 5px;
}

#keyboard li:last-child {
  border-radius: 0 5px 5px 5px;
}

The difference is subtle but effective:

Next, we apply the stylings that differentiate the white and black keys. Notice that the white keys have a z-index of 1 and the black keys have a z-index of 2:

.white {
  height: 12.5rem;
  width: 3.5rem;
  z-index: 1;
  border-left: 1px solid hsl(0, 0%, 73%);
  border-bottom: 1px solid hsl(0, 0%, 73%);
  border-radius: 0 0 5px 5px;
  box-shadow: -1px 0 0 var(--white-80) inset, 0 0 5px hsl(0, 0%, 80%) inset,
    0 0 3px var(--black-20);
  background: linear-gradient(to bottom, hsl(0, 0%, 93%) 0%, white 100%);
  color: var(--black-30);
}

.black {
  height: 8rem;
  width: 2rem;
  margin: 0 0 0 -1rem;
  z-index: 2;
  border: 1px solid black;
  border-radius: 0 0 3px 3px;
  box-shadow: -1px -1px 2px var(--white-20) inset,
    0 -5px 2px 3px var(--black-60) inset, 0 2px 4px var(--black-50);
  background: linear-gradient(45deg, hsl(0, 0%, 13%) 0%, hsl(0, 0%, 33%) 100%);
  color: var(--white-50);
}

When a key is pressed, we’ll use JavaScript to add a class of "pressed" to the relevant li element. For now, we can test this by adding the class directly to our HTML elements.

.white.pressed {
  border-top: 1px solid hsl(0, 0%, 47%);
  border-left: 1px solid hsl(0, 0%, 60%);
  border-bottom: 1px solid hsl(0, 0%, 60%);
  box-shadow: 2px 0 3px var(--black-10) inset,
    -5px 5px 20px var(--black-20) inset, 0 0 3px var(--black-20);
  background: linear-gradient(to bottom, white 0%, hsl(0, 0%, 91%) 100%);
  outline: none;
}

.black.pressed {
  box-shadow: -1px -1px 2px var(--white-20) inset,
    0 -2px 2px 3px var(--black-60) inset, 0 1px 2px var(--black-50);
  background: linear-gradient(
    to right,
    hsl(0, 0%, 27%) 0%,
    hsl(0, 0%, 13%) 100%
  );
  outline: none;
}

Certain white keys need to be moved toward the left so that they sit under the black keys. We give these a class of "offset" in our HTML, so we can keep the CSS simple:

.offset {
  margin: 0 0 0 -1rem;
}

If you’ve followed the CSS up to this point, you should have something like this:

Finally, we’ll style the keyboard itself:

#keyboard {
  height: 15.25rem;
  width: 41rem;
  margin: 0.5rem auto;
  padding: 3rem 0 0 3rem;
  position: relative;
  border: 1px solid var(--keyboard-border);
  border-radius: 1rem;
  background-color: var(--keyboard);
  box-shadow: 0 0 50px var(--black-50) inset, 0 1px var(--keyboard-shadow) inset,
    0 5px 15px var(--black-50);
}

We now have a nice-looking CSS keyboard, but it’s not interactive and it doesn’t make any sounds. To do this, we’ll need JavaScript.

Musical JavaScript

To create the sounds for our synth, we don’t want to rely on audio samples — that’d be cheating! Instead, we can use the web’s AudioContext API, which has tools that can help us turn digital waveforms into sounds.

To create a new audio context, we can use:

const audioContext = new (window.AudioContext || window.webkitAudioContext)();

Before using our audioContext it will be helpful to select all our note elements in the HTML. We can use this helper to easily query the elements:

const getElementByNote = (note) =>
  note && document.querySelector(`[note="${note}"]`);

We can then store the elements in an object, where the key of the object is the key that a user would press on the keyboard to play that note.

const keys = {
  A: { element: getElementByNote("C"), note: "C", octaveOffset: 0 },
  W: { element: getElementByNote("C#"), note: "C#", octaveOffset: 0 },
  S: { element: getElementByNote("D"), note: "D", octaveOffset: 0 },
  E: { element: getElementByNote("D#"), note: "D#", octaveOffset: 0 },
  D: { element: getElementByNote("E"), note: "E", octaveOffset: 0 },
  F: { element: getElementByNote("F"), note: "F", octaveOffset: 0 },
  T: { element: getElementByNote("F#"), note: "F#", octaveOffset: 0 },
  G: { element: getElementByNote("G"), note: "G", octaveOffset: 0 },
  Y: { element: getElementByNote("G#"), note: "G#", octaveOffset: 0 },
  H: { element: getElementByNote("A"), note: "A", octaveOffset: 1 },
  U: { element: getElementByNote("A#"), note: "A#", octaveOffset: 1 },
  J: { element: getElementByNote("B"), note: "B", octaveOffset: 1 },
  K: { element: getElementByNote("C2"), note: "C", octaveOffset: 1 },
  O: { element: getElementByNote("C#2"), note: "C#", octaveOffset: 1 },
  L: { element: getElementByNote("D2"), note: "D", octaveOffset: 1 },
  P: { element: getElementByNote("D#2"), note: "D#", octaveOffset: 1 },
  semicolon: { element: getElementByNote("E2"), note: "E", octaveOffset: 1 }
};

I found it useful to specify the name of the note here, as well as an octaveOffset, which we’ll need when working out the pitch.

We need to supply a pitch in Hz. The equation used to determine pitch is x * 2^(y / 12) where x is the Hz value of a chosen note — usually A₄, which has a pitch of 440Hz — and y is the number of notes above or below that pitch.

That gives us something like this in code:

const getHz = (note = "A", octave = 4) => {
  const A4 = 440;
  let N = 0;
  switch (note) {
    default:
    case "A":
      N = 0;
      break;
    case "A#":
    case "Bb":
      N = 1;
      break;
    case "B":
      N = 2;
      break;
    case "C":
      N = 3;
      break;
    case "C#":
    case "Db":
      N = 4;
      break;
    case "D":
      N = 5;
      break;
    case "D#":
    case "Eb":
      N = 6;
      break;
    case "E":
      N = 7;
      break;
    case "F":
      N = 8;
      break;
    case "F#":
    case "Gb":
      N = 9;
      break;
    case "G":
      N = 10;
      break;
    case "G#":
    case "Ab":
      N = 11;
      break;
  }
  N += 12 * (octave - 4);
  return A4 * Math.pow(2, N / 12);
};

Although we’re only using sharps in the rest of our code, I decided to include flats here as well, so this function could easily be re-used in a different context.

For anyone who’s unsure about musical notation, the notes A# and Bb, for example, describe the exact same pitch. We might choose one over another if we’re playing in a particular key, but for our purposes, the difference doesn’t matter.

Playing notes

We’re ready to start playing some notes!

First, we need some way of telling which notes are playing at any given time. Let’s use a Map to do this, as its unique key constraint can help prevent us from triggering the same note multiple times in a single press. Plus, a user can only click one key at a time, so we can store that as a string.

const pressedNotes = new Map();
let clickedKey = "";

We need two functions, one to play a key — which we’ll trigger on keydown or mousedown — and another to stop playing the key — which we’ll trigger on keyup or mouseup.

Each key will be played on its own oscillator with its own gain node (used to control the volume) and its own waveform type (used to determine the timbre of the sound). I’m opting for a "triangle" waveform, but you can use whatever you prefer of "sine", "triangle", "sawtooth" and "square". The spec offers a little more information on these values.

const playKey = (key) => {
  if (!keys[key]) {
    return;
  }

  const osc = audioContext.createOscillator();
  const noteGainNode = audioContext.createGain();
  noteGainNode.connect(audioContext.destination);
  noteGainNode.gain.value = 0.5;
  osc.connect(noteGainNode);
  osc.type = "triangle";

  const freq = getHz(keys[key].note, (keys[key].octaveOffset || 0) + 4);

  if (Number.isFinite(freq)) {
    osc.frequency.value = freq;
  }

  keys[key].element.classList.add("pressed");
  pressedNotes.set(key, osc);
  pressedNotes.get(key).start();
};

Our sound could do with some refinement. At the moment, is has a slightly piercing, microwave-buzzer quality to it! But this is enough to get started. We’ll come back and make some tweaks at the end!

Stopping a key is a simpler task. We need to let each note “ring out” for an amount of time after the user lifts their finger (two seconds is about right), as well as make the necessary visual change.

const stopKey = (key) => {
  if (!keys[key]) {
    return;
  }
  
  keys[key].element.classList.remove("pressed");
  const osc = pressedNotes.get(key);

  if (osc) {
    setTimeout(() => {
      osc.stop();
    }, 2000);

    pressedNotes.delete(key);
  }
};

All that’s left is to add our event listeners:

document.addEventListener("keydown", (e) => {
  const eventKey = e.key.toUpperCase();
  const key = eventKey === ";" ? "semicolon" : eventKey;
  
  if (!key || pressedNotes.get(key)) {
    return;
  }
  playKey(key);
});

document.addEventListener("keyup", (e) => {
  const eventKey = e.key.toUpperCase();
  const key = eventKey === ";" ? "semicolon" : eventKey;
  
  if (!key) {
    return;
  }
  stopKey(key);
});

for (const [key, { element }] of Object.entries(keys)) {
  element.addEventListener("mousedown", () => {
    playKey(key);
    clickedKey = key;
  });
}

document.addEventListener("mouseup", () => {
  stopKey(clickedKey);
});

Note that, while most of our event listeners are added to the HTML document, we can use our keys object to add click listeners to the specific elements we have already queried. We also need to give some special treatment to our highest note, making sure we convert the ";" key into the spelled-out "semicolon" used in our keys object.

We can now play the keys on our synth! There’s just one problem. The sound is still pretty shrill! We might want to knock down the octave of the keyboard by changing the expression that we assign to the freq constant:

const freq = getHz(keys[key].note, (keys[key].octaveOffset || 0) + 3);

You might also be able to hear a “click” at the beginning and end of the sound. We can solve this by quickly fading in and more gradually fading out of each sound.

In music production, we use the term attack to describe how quickly a sound goes from nothing to its maximum volume, and “release” to describe how long it takes for a sound to fade to nothing once it’s no longer played. Another useful concept is decay, the the time taken for sound to go from its peak volume to its sustained volume. Thankfully, our noteGainNode has a gain property with a method called exponentialRampToValueAtTime, which we can use to control attack, release and decay. If we replace our previous playKey function with the following one, we’ll get a much nicer plucky sound:

const playKey = (key) => {
  if (!keys[key]) {
    return;
  }

  const osc = audioContext.createOscillator();
  const noteGainNode = audioContext.createGain();
  noteGainNode.connect(audioContext.destination);

  const zeroGain = 0.00001;
  const maxGain = 0.5;
  const sustainedGain = 0.001;

  noteGainNode.gain.value = zeroGain;

  const setAttack = () =>
    noteGainNode.gain.exponentialRampToValueAtTime(
      maxGain,
      audioContext.currentTime + 0.01
    );
  const setDecay = () =>
    noteGainNode.gain.exponentialRampToValueAtTime(
      sustainedGain,
      audioContext.currentTime + 1
    );
  const setRelease = () =>
    noteGainNode.gain.exponentialRampToValueAtTime(
      zeroGain,
      audioContext.currentTime + 2
    );

  setAttack();
  setDecay();
  setRelease();

  osc.connect(noteGainNode);
  osc.type = "triangle";

  const freq = getHz(keys[key].note, (keys[key].octaveOffset || 0) - 1);

  if (Number.isFinite(freq)) {
    osc.frequency.value = freq;
  }

  keys[key].element.classList.add("pressed");
  pressedNotes.set(key, osc);
  pressedNotes.get(key).start();
};

We should have a working, web-ready synth at this point!

The numbers inside our setAttack, setDecay and setRelease functions may seem a bit random, but really they’re just stylistic choices. Try changing them and seeing what happens to the sound. You may end up with something you prefer!

If you’re interested in taking the project further, there are lots of ways you could improve it. Perhaps a volume control, a way to switch between octaves, or a way to choose between waveforms? We could add reverb or a low pass filter. Or perhaps each sound could be made up of multiple oscillators?

For anyone interested in understanding more about how to implement music theory concepts on the web, I recommend looking at the source code of the tonal npm package.


The post How to Code a Playable Synth Keyboard appeared first on CSS-Tricks. You can support CSS-Tricks by being an MVP Supporter.

Scalable AI and API Architectures in Python

Architectural Tenets

The architecture of Zato reflects several key foundational concepts underlying the design of the platform. Each component of the architecture takes each of the concepts into account.

The tenets are what drives the design of Zato, this is what directly leads to what its architecture looks like.

Google Cloud Pub/Sub – Overview

Introduction

The Google Cloud Pub/Sub is a fully managed real-time messaging service and it helps our applications/services to send and receive messages independently between them. It helps us to build robust and scalable applications by integrating them asynchronously. It provides scalability, resilience and handles millions of messages simultaneously.

Why Google Cloud Pub/Sub?

Google Cloud Pub/Sub can be used for a lot of use cases. In general, If you want to process large amounts of data for analytics or do you want to simplify the event-driven microservices development?, then the Google Cloud Pub-Sub is the right choice.

NoOps: What Does the Future Hold for DevOps Engineers?

With cloud adoption on the rise, the level of abstraction in application architecture has increased — from traditional on-premises servers to containers and serverless deployments. The focus on automation has also increased to the point where manual intervention is no longer preferred, even for infrastructure-related activities like backups, security management, and patch updates. This desired state equates to a NoOps environment, which involves smaller teams that can manage your application lifecycle. Ideally, in such an environment, the efforts required by your operations team will be eliminated.

It is beyond debate that DevOps is now deeply integrated into the DNA of all cloud-first organizations and is today more of a norm than a rarity. Cloud applications demand agility, and DevOps delivers it. However, does NoOps mean the end of the DevOps era? Or is it simply the next step in the progression of DevOps?