Ads
Saturday, July 31, 2021
Isolated Thunderstorms today!
With a high of F and a low of 57F. Currently, it's 65F and Cloudy outside.
Current wind speeds: 7 from the Northeast
Pollen: 6
Sunrise: July 31, 2021 at 05:51PM
Sunset: August 1, 2021 at 08:04AM
UV index: 0
Humidity: 88%
via https://ift.tt/2livfew
August 1, 2021 at 10:03AM
Friday, July 30, 2021
Mostly Clear today!
With a high of F and a low of 64F. Currently, it's 75F and Clear outside.
Current wind speeds: 5 from the Southeast
Pollen: 6
Sunrise: July 30, 2021 at 05:50PM
Sunset: July 31, 2021 at 08:05AM
UV index: 0
Humidity: 40%
via https://ift.tt/2livfew
July 31, 2021 at 10:03AM
A Shared ESLint Configuration
Looks like it was almost 9 years ago when Airbnb first published their JavaScript Style Guide. 112k stars on GitHub later, it seems like the de facto preset for Babel / ES Lint. But it’s not the only company out there with public ES Lint setups. Katy recently shared Mapbox’s setup.
ESLint plugins will help keep your code consistent and improve the quality, but they are also excellent teaching tools. When I come across a plugin, I take joy in reading each rule to learn the benefits of enabling or disabling it.
Katy DeCorah, “A shared ESLint configuration”
Sophisticated linting as-you-author is one of those things that has really upped the game of development over the years.
Direct Link to Article — Permalink
The post A Shared ESLint Configuration appeared first on CSS-Tricks. You can support CSS-Tricks by being an MVP Supporter.
from CSS-Tricks https://ift.tt/2Vq2kHa
via IFTTT
White-label SaaS shipping startup Outvio closes $3M round led by Change Ventures
Outvio, an Estonian startup that provides a white-label SaaS fulfillment solution for medium-sized and large online retailers in Spain and Estonia, has closed a $3 million early-stage financing round led by Change Ventures. Also participating were TMT Investments (London), Fresco Capital (San Francisco), and Lemonade Stand (Tallinn). Several angels also joined the round including James Berdigans (Printify) and Kristjan Vilosius (Katana MRP). This is the startup’s first institutional round of funding, after bootstrapping since 2018.
Online retailers usually have to use a number of different tools or hire expensive developers to create in-house shipping solutions. Outvio offers online stores of any size a post-purchase shipping experience, which seeks to replicate an Amazon-style experience where customers can also return packages. Among others, itcompetes with ShippyPro, which runs out of Italy and has raised $5 million to date.
Juan Borras, co-founder and CEO of Outvio said: “We can give any online store all the tools needed to offer a superior post-sale customer experience. We can integrate at different points in their fulfilment process, and for large merchants, save them hundreds of thousands in development costs alone.”
He added: “What happens after the purchase is more important than most shops realize. More than 88% of consumers say it is very important for them that retailers proactively communicate every fulfilment and delivery stage. Not doing so, especially if there are problems, often results in losing that client. Our mission is to help online stores streamline everything that happens after the sale, fueling repeat business and brand-loyal customers with the help of a fantastic post-purchase experience.”
Rait Ojasaar, Investment Partner at lead investor Change Ventures commented: “While online retailing has a long way to go, the expectations of consumers are increasing when it comes to delivery time and standards. The same can be said about the online shop operators who increasingly look for more advanced solutions with consumer-like user experience. The Outvio team has understood exactly what the gap in the market is and has done a tremendous job of finding product-market fit with their modern fulfilment SaaS platform.”
from Amazon – TechCrunch https://ift.tt/3BQs8g6
via IFTTT
EU hits Amazon with record-breaking $887M GDPR fine over data misuse
Luxembourg’s National Commission for Data Protection (CNPD) has hit Amazon with a record-breaking €746 million ($887m) GDPR fine over the way it uses customer data for targeted advertising purposes.
Amazon disclosed the ruling in an SEC filing on Friday in which it slammed the decision as baseless and added that it intended to defend itself “vigorously in this matter.”
“Maintaining the security of our customers’ information and their trust are top priorities,” an Amazon spokesperson said in a statement. “There has been no data breach, and no customer data has been exposed to any third party. These facts are undisputed.
“We strongly disagree with the CNPD’s ruling, and we intend to appeal. The decision relating to how we show customers relevant advertising relies on subjective and untested interpretations of European privacy law, and the proposed fine is entirely out of proportion with even that interpretation.”
The penalty is the result of a 2018 complaint by French privacy rights group La Quadrature du Net, a group that claims to represent the interests of thousands of Europeans to ensure their data isn’t used by big tech companies to manipulate their behavior for political or commercial purposes. The complaint, which also targets Apple, Facebook Google and LinkedIn and was filed on behalf of more than 10,000 customers, alleges that Amazon manipulates customers for commercial means by choosing what advertising and information they receive.
La Quadrature du Net welcomed the fine issued by the CNPD, which “comes after three years of silence that made us fear the worst.”
“The model of economic domination based on the exploitation of our privacy and free will is profoundly illegitimate and contrary to all the values that our democratic societies claim to defend,” the group added in a blog post published on Friday.
The CNPD has also ruled that Amazon must commit to changing its business practices. However, the regulator has not publicly committed on its decision, and Amazon didn’t specify what revised business practices it is proposing.
The record penalty, which trumps the €50 million GDPR penalty levied against Google in 2019, comes amid heightened scrutiny of Amazon’s business in Europe. In November last year, the European Commission announced formal antitrust charges against the company, saying the retailer has misused its position to compete against third-party businesses using its platform. At the same time, the Commission a second investigation into its alleged preferential treatment of its own products on its site and those of its partners.
from Amazon – TechCrunch https://ift.tt/3iXkEiE
via IFTTT
Conjuring Generative Blobs With The CSS Paint API
The CSS Paint API (part of the magical Houdini family) opens the door to an exciting new world of design in CSS. Using the Paint API, we can create custom shapes, intricate patterns, and beautiful animations — all with a touch of randomness — in a way that is portable, fast, and responsive.
We are going to dip our toes into the bubbling cauldron of generative CSS magic by creating my favorite shape, the blob. Random blobs are a great starting point for anyone new to generative art/design, and we will be learning the CSS Paint API as we go, so this is an ideal starting point for folks who are new to this world. You’ll be a generative CSS magician in no time!
Let’s hop on our broomsticks and conjure up some shapes.
Generative?
For some folks reading this, generative art may be an unfamiliar topic. If you are already comfortable with generative art/design, feel free to hop down to the next section. If not, here’s a little example:
Imagine, for a moment, that you are sitting at a desk. You have three stamps, some dice, and a piece of paper. Each of the stamps has a different shape on it. There is a square, a line, and a circle. You roll the dice. If the dice land on one, you use the square stamp on the page. If the dice lands on two, you use the line stamp. If it lands on three, you use the circle stamp. If the dice reads four, five, or six, you do nothing. You repeat the roll-and-stamp process until the page fills with shapes — this is generative art!
It can seem a little scary at first, but really, that’s all “generative” means — something created with an element of chance/unpredictability. We define some rules and let a source of randomness guide us to an outcome. In the “analog” example above, the randomness source is some dice. When we are working in the browser, it could be Math.random()
or another similar function.
To bring things back to the land of ones and zeros for a moment, this is what the above example would look like if written in code:
Pretty cool, eh? By defining some simple rules and actioning them at random, we have created a unique pattern. In this tutorial series, we will use generative techniques just like this to create exciting user interfaces.
What is the CSS Paint API, and what’s a worklet?
The CSS Paint API allows us low-level access to CSS itself(!) through an HTML5 <canvas>
-like drawing API. We can harness this power with something called a worklet.
Worklets, in short, are JavaScript classes. Each worklet class must have a paint()
function. A worklet’s paint()
function can programmatically create an image for any CSS property that expects one.
For example:
.my-element {
background-image: paint(texture);
}
Here, we have a fictional texture
worklet that generates a beautiful (I’ll leave this up to your imagination), programmatic texture. Where we might usually assign a url(...)
value to the background-image
property, we instead call paint(worklet_name)
— this runs the worklet’s paint()
function and renders the output to the target element.
We will be getting into how to write worklets in detail shortly, but I wanted to give you a quick primer on what they are before I start talking about them.
What we are building
So, in this tutorial, we will be building a generative blob worklet. Our worklet will take a few input parameters (as CSS Custom Properties, more on this a little later) and return a beautiful, random blob shape.
Let’s get started by checking out some examples of the finished worklet in action — if a picture paints a thousand words, a CodePen must paint a million, right?
The blob worklet, as a background image
First, here’s a demo of the blob worklet just hanging out on its own, generating a value for the background-image
property of an element:
I encourage you to look at the CSS for the above CodePen, change the custom properties, resize the element, and see what happens. See how the shape resizes fluidly and updates when the custom properties change? Don’t worry about understanding how this works right now. At this stage, we are only concerned with what we are building.
Generative image masks, a practical use case
Awesome, now that we have seen the “standalone” worklet, let’s check out how we can use it. In this example, the worklet functions as a generative image mask:
The result (I think) is rather striking. The worklet adds a natural, eye-catching curve to the design. In addition, the mask shape is different each time the page loads, which is a fantastic way to keep the UI fresh and exciting — click “rerun” on the CodePen above to see this effect in action. This ever-changing behavior is subtle, for sure, but I hope it will bring folks who notice it a little bit of joy. The web can be quite a cold, sterile place, and generative touches like this can make it feel a lot more organic!
Note: I’m certainly not suggesting we all start making our entire interfaces change at random. That would be terrible for usability! This kind of behavior works best when applied sparingly and only to presentational elements of your website or app. Think blog post headers, hero images, subtle background patterns, etc.
Now, this is just one example (and simple one, at that), but I hope it gives you some ideas on how you could use the blob worklet in your own design and development. For anyone looking for some extra inspiration, a quick Dribbble search for “blobs” should give you a whole heap of ideas!
Wait, do I need the CSS Paint API to make blobs?
In short, no!
There are, in fact, a plethora of ways to make blobs to use in your UI design. You could reach for a tool like Blobmaker, do some magic with border-radius
, use a regular <canvas>
element, whatever! There are tons of roads leading to blob city.
None of these, however, are quite the same as using the CSS Paint API. Why?
Well, to name a few reasons…
It allows us to be expressive in our CSS
Instead of dragging around sliders, tweaking radii, or endlessly clicking “regenerate” in the hope that a perfect blob comes our way, we can use just a few human-readable values to get what we need.
For example, the blob worklet we will be building in this tutorial takes the following input properties:
.worklet-target {
--blob-seed: 123456;
--blob-num-points: 8;
--blob-variance: 0.375;
--blob-smoothness: 1;
--blob-fill: #000;
}
Need your blobs to be super subtle and minimal? Reduce the --blob-variance
custom property. Need them to be detailed and overstated? Bring it up!
Fancy redesigning your site in a more brutalist direction? No problem! Instead of re-exporting hundreds of assets or custom coding a bunch of border-radius
properties, simply reduce the --blob-smoothness
custom property to zero:
Handy, eh? The CSS Paint API, through worklets, allows us to create ever-unique UI elements that fit right in with a design system.
Note: I am using GSAP in the examples above to animate the input properties of the paint worklet we are building in this tutorial.
It is super performant
It just so happens that generative work can get a little heavy, computation-wise. We often find ourselves looping through lots of elements, performing calculations, and other fun stuff. When we factor in that we may need to create multiple programmatic, generative visuals on a page, performance issues could become a risk.
Luckily for us, CSS Paint API worklets do all their magic outside of the main browser thread. The main browser thread is where all of the JavaScript we usually write exists and executes. Writing code this way is perfectly OK (and generally preferable), **but it can have limitations. When we try and do too much on the main browser thread, the can UI become sluggish or even blocked.
As worklets run on a different thread to the main website or app, they will not “block” or slow down the interface. Additionally, this means that the browser can spin up lots of separate worklet instances that it can call on when needed — this is similar to containerization and results in blazing fast performance!
It won’t clutter the DOM
Because the CSS Paint API essentially adds an image to a CSS property, it doesn’t add any extra elements to the DOM. To me, this feels like a super clean approach to creating generative visual elements. Your HTML structure remains clear, semantic, and unpolluted, while your CSS handles how things look.
Browser support
It is worth noting that the CSS Paint API is a relatively new technology, and although support is growing, it is still unavailable in some major browsers. Here is a browser support table:
This browser support data is from Caniuse, which has more detail. A number indicates that browser supports the feature at that version and up.
Desktop
Chrome | Firefox | IE | Edge | Safari |
---|---|---|---|---|
65 | No | No | 79 | No |
Mobile / Tablet
Android Chrome | Android Firefox | Android | iOS Safari |
---|---|---|---|
92 | No | 92 | No |
Although browser support is still a little thin on the ground — in this tutorial, we will be looking at how to use the css-paint-polyfill maintained by GoogleChromeLabs to make sure users in all browsers can enjoy our creations.
We will, additionally, be looking at how to “fail gracefully” when the CSS Paint API is unsupported. A polyfill means extra JavaScript weight, so for some folks, it is not a viable solution. If this is you, don’t worry. We will be exploring browser support options for everyone.
Let’s code!
OK, OK! We know what we are building and why the CSS Paint API rocks — now let’s get coding! First things first, let’s get a development environment spun up.
Note: if you get a little lost at any point during in this tutorial, you can view a finished version of the worklet.
A simple development environment
To get us started, I have created a worklet-starter-kit repository. As a first step, pop on over to GitHub and clone it. Once you have cloned and navigated inside the repo, run:
npm install
Followed by:
npm run start
Once you have run the above commands, a simple development server fires up in the current directory, and your default browser opens. As worklets must be loaded either over HTTPS or from localhost
— this setup ensures that we can use our worklet without any CORS issues. The starter kit also handles automatically refreshing the browser when we make any changes.
As well as serving our content and providing a basic live reload, this repository features a simple build step. Powered by esbuild, this process bundles up any JavaScript imports
inside our worklet and outputs the result to a worklet.bundle.js
file. Any changes made in worklet.js
are automatically reflected in worklet.bundle.js
.
If you have a poke around the repository, you might notice that there is already some HTML and CSS kicking around. We have a simple index.html
file with a single worklet-canvas
div, alongside some CSS to center it on the page and scale it to the viewport. Think of this as a blank canvas for all of your worklet experimentation!
Initializing our worklet
OK, now that we have our development environment up and running, it’s time to create our worklet. Let’s start by navigating to the worklet.js
file.
Note: Remember, worklet.bundle.js
is automatically generated by our build step. We don’t ever want to edit this file directly.
In our worklet.js
file, we can define our Blob
class and register it with the registerPaint
function. We pass two values to registerPaint
— the name we would like our worklet to have (in our case, blob) and the class that defines it:
class Blob {}
registerPaint("blob", Blob);
Excellent! We just took the first step towards creating our blobs!
Adding a paint()
function
Now, not much is happening yet, so let’s add a simple paint()
function to our Blob
class to check things are working OK:
paint(ctx, geometry, properties) {
console.log(`Element size is ${geometry.width}x${geometry.height}`);
ctx.fillStyle = "tomato";
ctx.fillRect(0, 0, geometry.width, geometry.height);
}
We can think of this paint()
function like a callback. It runs, initially, when the worklet’s target element first renders. After this, any time the element’s dimensions change or the worklet’s input properties update, it runs again.
When the paint()
function is called, it automatically has a few values passed through it. In this tutorial, we are making use of the first three:
context
— a 2D drawing context similar to that of a<canvas>
element, we use this to draw things.geometry
— an object containing the width and height of the target elementproperties
— an array of custom properties
Now that we have a simple paint()
function defined, let’s pop over to the index.html
file and load our worklet. To do so, we are going to add a new <script>
just before our closing </body>
tag:
<script>
if (CSS["paintWorklet"] !== undefined) {
CSS.paintWorklet.addModule("./worklet.bundle.js");
}
</script>
Note: we are registering the bundled version of our worklet!
Excellent. Our blob
worklet is now loaded and ready for use in our CSS. Let’s use it to generate a background-image
for our worklet-canvas
class:
.worklet-canvas {
background-image: paint(blob);
}
Once you have added the above snippet, you should see a red square. Our worklet is alive! Nice work. If you resize the browser window, you should see the worklet-canvas
element’s dimensions printed in the browser console. Remember, the paint()
function runs whenever the worklet target’s dimensions change.
Defining the worklet’s input properties
To allow our worklet to generate beautiful blobs, we need to help it out and pass it some properties. The properties we need are:
--blob-seed
— a “seed” value for a pseudorandom number generator; more on this in a moment--blob-num-points
— how detailed the blob is based on the number of points used along the shape--blob-variance
— how varied the blob’s control points are--blob-smoothness
— the smoothness/sharpness of the blob’s edges--blob-fill
— the blob’s fill color
Let’s tell our worklet that it will receive these properties and that it needs to watch them for changes. To do so, we can head back over to our Blob
class and add an inputProperties
getter:
static get inputProperties() {
return [
"--blob-seed",
"--blob-num-points",
"--blob-variance",
"--blob-smoothness",
"--blob-fill",
];
}
Cool. Now that our worklet knows what input properties to expect, we should add them to our CSS:
.worklet-canvas {
--blob-seed: 123456;
--blob-num-points: 8;
--blob-variance: 0.375;
--blob-smoothness: 1;
--blob-fill: #000;
}
Now, at this point, we could use the CSS Properties and Values API (another member of the Houdini family) **to assign some defaults and make these custom properties a little easier to parse in our worklet. Unfortunately, however, at this moment, the Properties and Values API does not have the best browser support.
For now, to keep things simple, we are going to leave our custom properties as they are — relying on some basic parsing functions in our worklet instead.
Heading back to our worklet class for a moment, let’s add these utility functions:
propToString(prop) {
return prop.toString().trim();
}
propToNumber(prop) {
return parseFloat(prop);
}
In the absence of the Properties and Values API, these simple utility functions will help us convert the properties
passed to paint()
to usable values.
Using our new helper functions, we can parse properties
and define some variables to use in our paint()
function. Let’s remove the old “debug” code, too:
paint(ctx, geometry, properties) {
const seed = this.propToNumber(properties.get("--blob-seed"));
const numPoints = this.propToNumber(properties.get("--blob-num-points"));
const variance = this.propToNumber(properties.get("--blob-variance"));
const smoothness = this.propToNumber(properties.get("--blob-smoothness"));
const fill = this.propToString(properties.get("--blob-fill"));
}
If you log any of these variables, you should see that the properties
made available by the paint()
function map exactly to the Custom Properties we defined in our CSS a moment ago.
If you open up dev-tools, inspect the worklet-canvas
element, and change any of these custom properties — you should see that the logs re-run and reflect the updated value. Why? Our worklet reacts to any changes to its input properties and re-runs its paint()
function when it detects them.
OK, folks, it’s time to start forming our blob shape. To do this, we need a way of generating random numbers. After all, this is what will make our blobs generative!
Now, you may be thinking, “Hey, we can use Math.random()
for this!” and in many ways, you would be right on. There is, however, a problem with using a “regular” random number generator in CSS Paint API worklets. Let’s check it out.
The problem with Math.random()
We noticed earlier how a worklet’s paint()
function runs rather often. If we use a method such as Math.random()
to generate random values within paint()
— they will be different each time the function executes. Different random numbers mean a different visual result every time the worklet re-renders. We do not want this at all. Sure, we want our blobs to be random, but only at the point of conception. They shouldn’t change once they exist on the page unless we explicitly tell them to do so.
I found this concept a little tricky to get my head around at first, so I have made a couple of CodePens (best viewed in a browser that natively supports the CSS Paint API) to help demonstrate. In the first example, we have a worklet that sets a random background color, using Math.random()
:
Warning: resizing the element below will result in a flash of color.
Try resizing the element above and notice how the background color changes as it updates. For some niche applications and fun demos, this might be what you want. In most practical use-cases, though, it isn’t. Aside from being visually jarring, behavior like this could be an accessibility issue for users who are sensitive to motion. Imagine that your worklet contained hundreds of dots that all started flying around and flashing whenever something on the page changed size!
Luckily for us, this issue is quite simple to fix. The solution? A pseudorandom number generator! Pseudorandom number generators (or PRNGs) generate random numbers based on a seed. Given the same seed value, a PRNG always returns the same sequence of random numbers — this is perfect for us, as we can re-initialize the PRNG every time the paint()
function runs, ensuring the same sequence of random values!
Here’s a CodePen demonstrating how a PRNG works:
Click “generate” to choose some random numbers — then, click “generate” a few more times. Notice how the sequence of numbers is the same each time you click? Now, try changing the seed value, and repeat this process. The numbers will be different from the previous seed value, but consistent across generations. This is the beauty of a PRNG. Predictable randomness!
Here’s the random-background-color CodePen again, using a PRNG rather than Math.random()
:
Ah! Much better! The element has a random color set when the page loads, but the background color does not change when it resizes. Perfect! You can test this out by clicking “Rerun” on the CodePen above, and resizing the element.
Adding pseudorandom numbers to our worklet
Let’s go ahead and add a PRNG function above our Blob
class definition:
// source: https://github.com/bryc/code/blob/master/jshash/PRNGs.md
function mulberry32(a) {
return function () {
a |= 0;
a = (a + 0x6d2b79f5) | 0;
var t = Math.imul(a ^ (a >>> 15), 1 | a);
t = (t + Math.imul(t ^ (t >>> 7), 61 | t)) ^ t;
return ((t ^ (t >>> 14)) >>> 0) / 4294967296;
};
}
Now, I’d be lying if I said I understand quite literally anything this function is doing. I discovered this beautiful little snippet of code through Jake Archibald’s excellent article on being predictably random with the CSS Paint API, and have used it in a ton of work since. You can find the original repository for this function over at GitHub — it includes a whole heap of excellent PRNGs and is certainly worth a look.
Note: while I don’t fully understand how this function works, I know how to use it. Often, when working in the generative world (if you are anything like me, anyway!), you will find yourself in this situation. When you do, don’t worry! It is absolutely OK to use a snippet of code to create some art/design without knowing exactly how it works. We can learn by doing, and that’s awesome.
OK, great, we have a PRNG function. Let’s add it to paint()
:
const random = mulberry32(seed);
In this snippet, we call mulberry32()
with our --blob-seed
custom property as its seed value, and it returns a brand new function. This new function — random
— returns a random number between zero and one.
Lovely, let’s put our shiny new PRNG to use.
A quick aside: Drawing with the CSS Paint API
When working with CSS Paint API worklets, just like HTML <canvas>
, we draw everything inside a 2D context. This context has a width and a height. For worklets, the width and height of this context always matches that of the element the worklet is painting to.
Say, for example, we wanted to add a point to the center of a 1920x1080px
context, we could visualize it like so:
As we begin to write our “render” code, this is good to keep in mind.
How our blob is formed, an overview
Before we write any code, I’d like to show you a little SVG animation of how we will create our blob shape. If you are a visual learner like me, you may find an animated reference helpful for understanding this kind of thing:
To break this process down into three steps:
- Plot several equally spaced points around the radius of a circle.
- Pull each point a random amount towards the center of the circle.
- Draw a smooth curve through each of the points.
Now, things are about to get a tiny bit maths-y but don’t worry. We’ve got this!
Defining the blob’s control points
To start, let’s define the radius
of our blob. The blob’s radius determines how large or small it is.
We want our blob shape to always “fit” inside the element it is painted on. To ensure this is the case, we check the width and height of the worklet’s target element and set the blob’s radius accordingly. Our blob is essentially a weird circle, and a circle’s total width/height will always be equal to its radius multiplied by two, so we divide this value to match. Let’s add some code to achieve this in our paint()
function:
const radius = Math.min(geometry.width, geometry.height) / 2;
Here’s an image to help explain what is happening here:
Cool! Now that we know what the radius of our blob should be, we can initialize its points:
const points = [];
const center = {
x: geometry.width / 2,
y: geometry.height / 2,
};
const angleStep = (Math.PI * 2) / numPoints;
for (let i = 1; i <= numPoints; i++) {
const angle = i * angleStep;
const point = {
x: center.x + Math.cos(angle) * radius,
y: center.y + Math.sin(angle) * radius,
};
}
Phew! In this snippet, we “walk” around the circumference of a circle, plopping down some equally spaced points as we go. How does this work?
To start, we define an angleStep
variable. The maximum angle between two points on the circumference of a circle is Pi × 2
. By dividing Pi × 2
by the number of “points” we would like to create, we have the desired (equally spaced) angle between each point.
Next, we loop over each point. For each of these points, we define an angle
variable. This variable is our angleStep
multiplied by the point’s index. Given a radius, an angle, and a center point for a circle, we can use Math.cos()
and Math.sin()
to plot each point.
Note: If you would like to learn a little more about trigonometric functions, I wholeheartedly recommend Michelle Barker’s excellent series!
Now that we have some perfect, beautiful, equally spaced points positioned around the circumference of a circle — we should mess them up. To do so, we can “pull” each one, a random amount, towards the circle’s center.
How can we do this?
First, let’s add a new lerp
function (short for linear interpolation) just below where we defined mulberry32
:
function lerp(position, target, amt) {
return {
x: (position.x += (target.x - position.x) * amt),
y: (position.y += (target.y - position.y) * amt),
};
}
This function takes a start-point, an end-point, and an “amount” value between zero and one. The return value of this function is a new point, placed somewhere between the start and end points.
In our worklet, just below where we define the point
variable in our for-loop, we can use this lerp
function to “pull” the point towards the center position. We store the modified point in our points
array:
points.push(lerp(point, center, variance * random()));
For the linear interpolation amount, we use our --blob-variance
property multiplied by a random number generated by random()
— as random()
always returns a value between zero and one, this amount will always be somewhere between zero, and our --blob-variance
number.
Note: A higher --blob-variance
will result in crazier blobs, as each point can end up closer to the center.
Drawing the curve
So, we have our blob’s points stored in an array. Right now, though, they aren’t used for anything! For the final step in our blob creation process, we will draw a smooth curve through each of them.
To draw this curve, we are going to use something called a Catmull-Rom spline. A Catmull-Rom spline is, in short, a great way of drawing a smooth Bézier curve through any number of { x, y }
points. With a spline, we don’t have to worry about any tricky control point calculation. We pass in an array of points, and get a beautiful, organic curve back. No sweat.
Let’s head over to the start of our worklet.js
file and add the following import:
import { spline } from "@georgedoescode/generative-utils";
Then install the package like so:
npm i @georgedoescode/generative-utils
This spline
function is quite sizeable and a little complex. For this reason, I have packaged it up and added it to my generative-utils repository, a small collection of handy generative art utilities.
Once we have imported spline
— we can use it in our worklet’s paint()
function like this:
ctx.fillStyle = fill;
ctx.beginPath();
spline(points, smoothness, true, (CMD, data) => {
if (CMD === "MOVE") {
ctx.moveTo(...data);
} else {
ctx.bezierCurveTo(...data);
}
});
ctx.fill();
Note: Place this snippet just after your for-loop!
We pass in our points, --blob-smoothness
property, and a flag to let spline
know it should return a closed shape. In addition, we use our --blob-fill
custom property to set the fill color of the blob. Now, if we take a look at our browser window, we should see something like this!
Hooray! We did it! The spline
function has successfully drawn a smooth curve through each of our points, thus making a gorgeous (and random) blob shape. If you would like your blob to be a little less rounded, try reducing the --blob-smoothness
property.
Now, all we need to do is add a touch more randomness.
A random, random seed value
Right now, our blob’s PRNG seed is a fixed value. We defined this --blob-seed
custom property in our CSS earlier, with a value of 123456
— this is great, but it means that the random numbers generated by random()
and, therefore, the blob’s core shape, is always the same.
For some instances, this is ideal. You may not want your blobs to be random! You may want to choose some perfect seed values and use them across your site as part of a semi-generative design system. For other cases, though, you may want your blobs to be random — just like the image mask example I showed you earlier.
How can we do this? Randomize the seed!
Now, this isn’t quite as simple as it might seem. Initially, when I was working on this tutorial, I thought, “Hey, I can initialize the seed value in the Blob
class’s constructor!” Unfortunately, though, I was wrong.
Since the browser may spin up multiple instances of a worklet to handle calls to paint()
— one of several Blob
classes may end up rendering the blob! If we initialize our seed value inside the worklet class, this value will be different across instances, and could lead to the visual “glitching” we discussed earlier.
To test this out, add a constructor
function to your Blob
class with the following code inside:
constructor() {
console.log(`My seed value is ${Math.random()}`);
}
Now, check out your browser console, and resize the window. In most cases, you get multiple logs with different random values. This behavior is no good for us; we need our seed value to be constant.
To solve this issue, let’s add a little JavaScript on the main thread. I am popping this in the <script>
tag we created earlier:
document
.querySelector(".worklet-canvas")
.style.setProperty("--blob-seed", Math.random() * 10000);
Excellent! Now when refreshing the browser window, we should see a new blob shape each time.
For our simple demo, this is perfect. In a “real” application, you may want to create a .blob
class, target all instances of it on load, and update the seed value of each element. You could also experiment with setting the blob’s variance, number of points, and roundness properties to random values.
For this tutorial, though, that’s it! All we have left to do is make sure our code works OK for users in all browsers, or provide a suitable fallback for when it doesn’t.
Loading a polyfill
By adding a polyfill, our CSS Paint API code will work in all major browsers, with the cost of extra JavaScript weight. Here’s how we can update our CSS.paintWorklet.addModule
code to add one to our example:
(async function () {
if (CSS["paintWorklet"] === undefined) {
await import("https://unpkg.com/css-paint-polyfill");
}
CSS.paintWorklet.addModule("./worklet.bundle.js");
})();
Using this snippet, we only load the polyfill if the current browser does not support the CSS Paint API. Nice!
A CSS-based fallback
If extra JavaScript weight isn’t your vibe, that’s cool. I totally get it. Luckily, using @supports
, we can define a lightweight, CSS-only fallback for browsers that do not support the CSS Paint API. Here’s how:
.worklet-canvas {
background-color: var(--blob-fill);
border-radius: 49% 51% 70% 30% / 30% 30% 70% 70%;
}
@supports (background: paint(blob)) {
.worklet-canvas {
background-color: transparent;
border-radius: 0;
background-image: paint(blob);
}
}
In this snippet, we apply a background-color
and a blob-like border-radius
(generated by fancy border radius) to the target element. If the CSS Paint API is supported, we remove these values and use our worklet to paint a generative blob shape. Awesome!
The end of the road
Well, folks, we’re all done. To quote the Grateful Dead — what a long, strange trip it’s been!
I know, there’s a lot to take in here. We have covered core generative art concepts, learned all about the CSS Paint API, and made some awesome generative blobs while we were at it. Not bad going at all, I say.
Now that we have learned the basics, though, we are ready to start creating all kinds of generative magic. Keep an eye out for more generative UI design tutorials from me soon, but in the meantime, try and take what we have learned in this tutorial and experiment! I’m sure you have a ton of fantastic ideas.
Until next time, fellow CSS magicians!
The post Conjuring Generative Blobs With The CSS Paint API appeared first on CSS-Tricks. You can support CSS-Tricks by being an MVP Supporter.
from CSS-Tricks https://ift.tt/3fbIXsa
via IFTTT
Thursday, July 29, 2021
Clear today!
With a high of F and a low of 64F. Currently, it's 72F and Clear outside.
Current wind speeds: 9 from the Southeast
Pollen: 6
Sunrise: July 29, 2021 at 05:49PM
Sunset: July 30, 2021 at 08:06AM
UV index: 0
Humidity: 48%
via https://ift.tt/2livfew
July 30, 2021 at 10:03AM
GitHub Explains the Open Graph Images
An explanation of those new GitHub social media images:
[…] our custom Open Graph image service is a little Node.js app that uses the GitHub GraphQL API to collect data, generates some HTML from a template, and pipes it to Puppeteer to “take a screenshot” of that HTML.
Jason Etcovich on The GitHub Blog in “A framework for building Open Graph images”
It’s so satisfying to produce templated images from HTML and CSS. It’s the perfect way to do social media images. If you’re doing it at scale like GitHub, there are a couple of nice tricks in here for speeding it up.
Direct Link to Article — Permalink
The post GitHub Explains the Open Graph Images appeared first on CSS-Tricks. You can support CSS-Tricks by being an MVP Supporter.
from CSS-Tricks https://ift.tt/35IiXiS
via IFTTT
Amazon’s Fire TV Cube now supports Zoom calls on your TV
Late last year, Amazon launched support for two-way calling that worked with its Fire TV Cube devices. The feature allowed consumers to make and receive calls from their connected TV to any other Alexa device with a screen. Today, the company is expanding this system to enable support for two-way calling with Zoom.
Starting today, Fire TV Cube owners (2nd gen.) will be able to join Zoom work meetings or virtual hangouts via their Fire TV Cube.
To take advantage of the new feature, you’ll need Amazon’s Fire TV Cube, its hands-free streaming device and smart speaker that has Alexa built in, as well as a webcam that supports USB Video Class (UVC) with at least 720p resolution and 30fps. But for a better experience, Amazon recommends a webcam with 1080p resolution and a 60-90 degree field of view from 6 to 10 feet away from the TV. It doesn’t recommend 4K webcams, however.
Amazon suggests webcams like the Logitech C920, C922x, C310, or the Wansview 101JD, for example.
You’ll then connect your webcam to your Fire TV Cube using a Micro USB to USB adapter.
For best results, you’ll want to attach the webcam above the TV screen, Amazon notes.
Once everything is set up and connected, you’ll need to download and install the Zoom app from the Fire TV Appstore. When joining meetings, you can either sign in as a guest or use an existing Zoom account, per the on-screen instructions.
Thanks to the Alexa integration, you can join your meetings hands-free, if you prefer, by way of a voice command like “Alexa, join my Zoom meeting.” Alexa will respond by prompting you for the meeting ID and passcode. Alternately, you can choose to use the remote control to enter in this information.
An optional feature also lets you sync your calendar to Alexa to allow the smart assistant to remind you about the upcoming meetings it finds on your calendar. If you go this route, Alexa will suggest the meeting to join and you’ll just have to say “yes” to be automatically dialed in.
Amazon first announced it was bringing video calling support to its Fire TV platform last fall — a significant update in the new era of remote work and schooling, driven by the pandemic. However, it’s not the only option on the market. Google also last year brought group video calls to its Hub Max devices, and later added support for Zoom calls. Meanwhile Facebook Portal devices have offered video calling of a more personal nature, and last year updated to support Zoom, too.
In other words, Amazon is playing a bit of catch-up here. And its solution is a little more unwieldy as it requires consumers to buy their own webcam, while something like Portal TV offers a TV with a smart camera included.
To use the new feature, you’ll need the latest Fire TV Cube software update to get started, Amazon notes.
from Amazon – TechCrunch https://ift.tt/3iY8A0N
via IFTTT
So you want to self-publish books and courses on programming
John Resig and I recently self-published our book on GraphQL. There are tons of how-tos for self-publishing a book, or even online classes, but very little in the way of why you would want to, or whether it’s even worth your while. I’m going to share my experience and revenue numbers with you in this post, as well as those from others who have self-published material. I’ll go specifically into the pros and cons of self-publishing books and courses in tech.
Revenue
This is probably what you’re most curious about, right? When I originally started working on our book, I sent a book proposal to publishers. But by the time John and I joined forces, we were both set on self-publishing. He had written two popular JavaScript books and a blog post about traditional publishing for programming books, which includes:
- Programmers aren’t that into reading books! Programming books, by and large, are poor sellers. They rarely sell more than 4,000 copies.
- He made $7,500 on his first 4,000 copies, where his royalty rate started at 10% for print sales and 20% for digital copies.
On the topic of traditional publishing revenue, Randall Kanna says: “Nothing comes from a tech book. Just the credibility.” A book can make significantly more, but it’s rare. Martin Kleppmann’s book on machine learning was O’Reilly’s second most popular seller in 2019, and he made $478,000 in the first three years (with 108,000 copies sold, a 10% royalty on print sales, and a 25% royalty on digital sales).
The Pragmatic Bookshelf is the outlying publisher when it comes to royalties: it gives authors 50% of gross profit. In their first 10 years operating, 42% of their authors made more than $50,000, and 12% made more than $100,000.
That said, self-publishing has much higher royalty rates:
- Amazon: 70% (for e-books; 60% minus printing cost for printed books)
- Leanpub: 80%
- Gumroad: 96.5% ($10 monthly membership fee)
- Your own website: 97%
This gives authors the potential to make more money. Discover Meteor was probably the most successful self-published programming book of its time, with around $500,000 in sales (9,000 copies) between 2013 (when they launched) and 2018 (when they made it available for free). The authors Sacha Grief and Tom Coleman put a lot of effort into marketing it (described in their Gumroad case study), and it became the recommended learning resource in the Meteor community. The current best-selling book is Adam Wathan and Steve Schoger’s Refactoring UI, which I believe passed $2 million in 2020! 🤑 Their success was also largely due to their ability to market the book, in addition to addressing a significant need for a broad audience (practical user interface design for front-end developers).
That’s books. Looking at publishing video courses, there are a few options:
- Some book publishers, like O’Reilly, have video platforms.
- YouTube is popular, of course. For example, Florin Pop, in his first year on YouTube, gained 74,000 subscribers and made $4,500 in ads from 1.6 million views.
- Video course platforms, like Egghead (which uses a royalty formula), Frontend Masters, Pluralsight, Linkedin Learning (formerly Lynda), and Udemy (with a 97% royalty rate on customers you refer and 37% on those who come through the platform).
- Egghead’s Wes Bos as a Service (WBaaS), like Epic React.
- Self publishing (hosting the content on your own site behind a paywall).
Like self-published books, self-published courses have a lot of potential. Level Up Tutorials, Kent C. Dodds, and Wes Bos don’t share revenue numbers for their courses, but I’m assuming they have made considerable sums. Wes, for example, has sold his courses to over 140,000 people at the time of writing!
Those are the outliers, of course. The majority of resources out there make significantly less. Take, for example, the self-published books in the GraphQL space that we were entering:
Title (Author) | Sales | Price | Estimated Revenue |
---|---|---|---|
Production Ready GraphQL (Marc-André Giroux) | 3,000 (this is a guess) | $49–$79 | $147,000–$237,000 |
The Road to GraphQL (Robin Wieruch) | 2,250 | $30–$80 | $67,500–$180,000 |
The GraphQL Guide (John Resig and Loren Sands-Ramshaw) | 1,000 | $30–$279 | $78,000 in sales; $68,000 in sponsorships |
Advanced GraphQL with Apollo & React (Mandi Wise) | 200 | $30–$45 | $10,000+ |
Fullstack GraphQL (Julian Mayorga) | 100 | $39 | $3,000 |
So, yes, the potential is big. But it’s not a guarantee.
Self-publishing pros and cons
Pros | Cons |
---|---|
Potential for more revenue. You get to set the price, sell different packages, and receive a larger cut. | It’s much harder. A publisher does a ton of things for you: editing, gathering feedback from technical reviewers, the build toolchain, translating, an online store, getting it on Amazon and other bookstores, customer service, tracking errata, etc. Doing all of these things yourself takes a lot of time, especially if you also decide to build a Gatsby site to display the text online. 😜 |
Flexibility. You get to decide what goes into the book as well as how it’s formatted and sold. | No built-in marketing or distribution. The success of your own book completely depends on your ability to market it. This is hard, as you can read in swyx’s notes on the topic. Books published traditionally usually sell more copies. |
Updates. You have the email addresses of your readers, and can send them updated versions of the book. | No print edition. While you can print on demand with some services, like Kindle Direct Publishing, most people don’t put in that effort. |
These pros and cons are for books. If you’re wondering about a breakdown of pros and cons specifically for self-published courses, they’re very similar because they face the same opportunities and challenges.
Should I create a book or course?
This is the big question. And while I wish I could give you a definitive answer one way or the other, it’s always going to be the same answer we love to give in tech: it depends.
Why would you want to self-publish a book or course? Here are some reasons:
- Income: It’s nice to put something out there and have it generate an income as long as it’s available.
- Positive impact: Creating a digital asset has high potential leverage. For example, if something takes you
X
amount of resources to create, and you distribute it to a thousand people who each gainY
amount of utility from learning with it, you’ve produced1000 * Y
utility in the world, which can be much larger thanX
. For more on this, I recommend Kleppmann’s post on the topic. - Reputation: Having written a book can help you get a job, gain clients, or simply elevate your reputation. Eve Porcello says publishing boosted her credibility—and as an added benefit, many readers hire her to teach workshops.
- Knowledge: If you’re like me, you’ll find that you learn much more about the topic you’re writing about than you knew when you started. I know I certainly did—I finally read the GraphQL spec, learned Vue and Android, ran into and solved a ton of bugs, and went through countless blog posts and conference talks. 😄
- Enjoyment: Some people enjoy the creative process. I liked learning, building example applications, and writing. In other words, there’s a level of self-fulfillment you can get from the work.
Those are the reasons why you might want to self-publish material. But whether you should actually do it depends on:
- Your writing ability: You absolutely need to be good at explaining complex concepts in simple terms that are easy for anyone to grasp. That’s a seriously high bar, especially if there’s existing good content on the topic.
- Your willingness to market: You need to be willing to get the word out, because no one will do it for you (at least at first). That takes some guts. I know there are many of people out there who have a tough time promoting themselves and their work.
- What it’s worth to you: You’ve seen a lot of the benefits that self-publishing content can offer, but are they worth it to you? Do impact, knowledge, and reputation motivate you? Maybe none of that matters and you’re simply looking for a labor of love. Whatever your motivation is, it’s important. Without it, you could lose steam and wind up with an unfinished project.
- The opportunity cost: What else would you do with your time and energy if you didn’t self-publish? Is that thing more valuable to you? Is there anything you’d regret missing out on because of this?
For me, while writing a book had an opportunity cost of lower income (compared to doing more consulting work), I’ve made a positive impact, increased my knowledge on a subject I care about, gained reputation, and enjoyed the process. And it also feels great when someone goes out of their way to tell me they’re “blown away” and appreciate reading my book. 😃🤗✨
Thanks to Chris Coyier, Geoff Graham, Sacha Greif, Robin Wieruch, Mandi Wise, Sebastian Grebe, Julian Mayorga, and Rachel Lake for providing input for this article.
The post So you want to self-publish books and courses on programming appeared first on CSS-Tricks. You can support CSS-Tricks by being an MVP Supporter.
from CSS-Tricks https://ift.tt/3rEM29c
via IFTTT
Developer-Friendly Passwordless Auth
I’d wager to say that most websites that are business-minded have accounts. A way to log into them. Social media sites, eCommerce sites, CMS systems, you name it, having accounts people log into is at the heart of them. So… make it good. That’s what Magic does (great name!).
Have you heard that language used in a sign-in system like “email me a magic link to sign in”? Well, now you know what can power it. But Magic isn’t just that, it’s all types of auth, including social logins and WebAuthn. Magic is a developer SDK that enables passwordless login in all these methods.
Magic is for teams of any size. Upon signing up, you’ll get $85 in credit which covers 10,000 logins, and each login is $0.0085 after that. That kind of pricing makes it extremely affordable for apps of any size. Small apps will have tiny (or no) bill, and by the time you have tens or hundreds of thousands of users, the cost will feel negligible. Especially considering all the time you saved by not rolling auth from scratch.
Why Magic? What does it offer?
Magic appeals to developers because:
- Superior developer experience. It’s easy to use and it’s fast to implement.
- Metered pricing — only pay for what you need. Also save money by avoiding the technical debt of your own auth.
- The ability to adapt to future authentication methods. Auth is always evolving.
- Don’t have to to deal with passwords — less security concerns.
- Next-gen security infastructure.
I really like all those, but especially #3. I think of it like image CDNs that offer optimization. The world of images is always evolving as well, and a good image CDN will evolve to support the latest formats and optimization techniques without any work on your end. So too with Magic and Auth.
The “J” and the “a” in Jamstack originally referred to “JavaScript” and “APIs”, which is exactly what Magic offers. Magic fits the Jamstack model very nicely. No server? No problem. Even though Magic absolutely has server-side offerings, and Jamstack could use things like cloud functions, you can get auth done entirely client-side if you’d like. Here’s a great (quick!) tutorial on that.
Here’s the most important thing though: Great UX. Users really like it when the auth of an app feels easy and is never a blocker for them using your app. That’s gonna help your conversion rates.
How do you implement Magic?
First, you need an account. I found it satisfying, of course, that they dog food their own auth signup process, giving you a taste for what you can have right away.
From here, you can scaffold an app out super quickly. The great DX continues here as they offer a way to scaffold out a working app right off the bat:
That’s a web-based starter, for which they have docs, examples, and live demos.
I was able to port a demo over to CodePen Projects super quickly. Check it out!
That’s just a client-side web version. The core of it is really this simple:
import { Magic } from 'magic-sdk'
const m = new Magic(API_KEY)
m.auth.loginWithMagicLink('user@email.address')
They’ve got server-side support for Node, Python, Ruby, PHP and Go. Magic is for apps of any scale, including incredibly security-sensitive apps. For example, you can even use client-side auth but then use AWS services, with their Hardware Security Modules (HSMs) and all.
Magic has SDK’s for React Native, iOS, Android, and of course native web. Then in addition to the email magic link style signup, they have social login support for Google, Facebook, Apple, GitHub, GitLab, Bitbucket, Linkedin, Twitter, and Discord. Phew! That’s a lot of support for a lot of things. Magic has you covered.
While I was plucking away with this and logging in myself, I could see all the action on my dashboard.
No Passwords
It’s notable that with Magic, there are literally no passwords. Magic email link flow means users need no passwords, and with social logins, users only need to be logged into that other service, not remember/save a password unique to your app. That’s the Magic thesis, which they spell out clearly in Passwords Suck:
Using passwords is a nightmare. No one wants to memorize yet another passphrase when our heads are already filled with them. Passwords are a huge vector for security breaches precisely because they place the burden of choosing unique and secure secrets on the user, who just can’t be bothered. We end up having one password for all the important stuff like banking, work, and school, one for the social-medias, and one for all the miscellaneous one-off services we don’t care too much about. The result is that a whopping 59% of people reuse their passwords across services, which means a leak anywhere quickly becomes a liability for the whole web.
Going password-less is good for users and good for the web.
Get Started
I’d encourage you to check it out. You can sign up for free, no credit card required, and if you do that today you’ll get 10,000 free logins on your account to try out. If you love it, and you have fellow industry folks you refer to Magic, you get 3,000 bonus logins — up to 90,000 in total.
The post Developer-Friendly Passwordless Auth appeared first on CSS-Tricks. You can support CSS-Tricks by being an MVP Supporter.
from CSS-Tricks https://ift.tt/3BWS3me
via IFTTT
Wednesday, July 28, 2021
Mostly Clear today!
With a high of F and a low of 65F. Currently, it's 78F and Clear outside.
Current wind speeds: 12 from the Southeast
Pollen: 5
Sunrise: July 28, 2021 at 05:48PM
Sunset: July 29, 2021 at 08:06AM
UV index: 0
Humidity: 40%
via https://ift.tt/2livfew
July 29, 2021 at 10:03AM
Facebook will require employees to be vaccinated before returning to campus
Earlier today, Google CEO Sundar Pichai announced that the company will require employees to be vaccinated before returning to work on-site. It was part of a larger letter sent to Google/Alphabet staff that also noted the company will be extending its work-from-home policy through October 18, as the Covid-19 Delta variant continues to sweep through the global population.
In a message to TechCrunch, Facebook’s VP of People, Lori Goler, confirmed a similar policy for the social media giant.
“As our offices reopen, we will be requiring anyone coming to work at any of our US campuses to be vaccinated,” Goler writes. “How we implement this policy will depend on local conditions and regulations. We will have a process for those who cannot be vaccinated for medical or other reasons and will be evaluating our approach in other regions as the situation evolves. We continue to work with experts to ensure our return to office plans prioritize everyone’s health and safety.”
The statement is worded similarly to the long letter penned by Pichai, which carved out an exception for “medical or other protected reasons.” The comment doesn’t offer an adjusted timeline for the return, which had initially planned to go half-capacity in September and full by October.
Last week, a spokesperson told The Wall Street Journal, “Expert guidelines state that vaccines are highly effective at preventing variants of COVID-19, including the Delta variant. Our timelines to reopen our offices haven’t changed.”
Both statements offer some wiggle room for the company, based on things like local and state regulations, medical or personal concerns and, presumably, access to the vaccine, which can vary greatly based on region.
Amazon also responded to TechCrunch’s inquiry on the matter, noting, “We strongly encourage Amazon employees and contractors to be vaccinated as soon as COVID-19 vaccines are available to them.”
The company’s current guidelines don’t appear to require vaccination in order to return to its offices, though unvaccinated employees are required to wear masks. Face coverings are optional for those who have verification of being fully vaccinated.
from Amazon – TechCrunch https://ift.tt/3iRhJbq
via IFTTT
Mostly Clear today!
With a high of F and a low of 15F. Currently, it's 14F and Clear outside. Current wind speeds: 13 from the Southwest Pollen: 0 S...
-
So you want an auto-playing looping video without sound? In popular vernacular this is the very meaning of the word GIF . The word has stuck...
-
With a high of F and a low of 31F. Currently, it's 37F and Cloudy outside. Current wind speeds: 7 from the Northeast Pollen: 0 S...
-
Last year , we kicked out a roundup of published surveys, research, and other findings from around the web. There were some nice nuggets in ...