Wavethrough – Stealing data from remote sites through (fake) wav files

Jake Archibald discovered a really nice browser bug (which is fixed by now) by which he was able to steal data from remote sites by loading it in as a (fake) wav file.

The exploit works as follows:

  1. Make a request to evil-script, using a Content-Range header to suggest there’s more data to be loaded afterwards.
  2. Have evil-script return a valid WAV PCM header block, but also have it return a Redirect response header to the cross-origin (!) location you want to read out.
  3. Since a Content-Range header was used, the browser will make a second request to fetch the rest of the data.
    • A browser susceptible to this exploit will actually make the request to the remote location defined in the Redirect header.
    • Good browsers will stop here, throwing a CORS error.
  4. Store the returned data in an <audio> element.
  5. Play back the audio fragment, and meanwhile read out its data using a ScriptProcessorNode.

Not all browsers were affected by this bug: in Firefox you could only get the length of the returned content, and it was only in Edge that Jake was able to read out the actual contents of the generated wav file. Here’s a video of Edge (warning: as it’s raw data you’ll only hear glitches and stuff … you might want to turn down the volume):

Nice find Jake!

A shame the process of reporting this bug with the Edge team didn’t go that smooth though (details in Jake’s post). I’m confident the Edge team will adjust / already have adjusted a few things internally to prevent this obstacle course from happening again.

Jake Archibald: “I discovered a browser bug” →

Other neat hacks that recently made rounds was this one, using the W3C Ambient Light Sensor API and this one using mix-blend-mode. Always fun to see smart people find a way to abuse a new technology that seems safe at first 🙂

Elsewhere , , , , Leave a comment

The Layouts of Tomorrow

With CSS Grid being available, Max Böck set out to find himself a layout challenge:

I went over to dribbble in search of layout ideas that are pushing the envelope a bit. The kind of design that would make frontend developers like me frown at first sight.

He settled on the “Digital Walls” shot (pictured above). The result is stunning. Next to CSS Grid it also uses CSS Parallax, CSS Scroll Snap Points, and Smooth Scrolling.

The Layouts of Tomorrow →

💁‍♂️ Be sure to also check out these magazine layouts recreated with CSS Grid.

Elsewhere , , , Leave a comment

React Native at Airbnb

Gabriel Peal, Android developer at Airbnb:

In 2016, we took a big bet on React Native. Two years later, we’re ready to share our experience with the world and show what’s next.

The result is a 5-part series of posts:

Yes that’s right, in part 4 he explains why they’re moving away from React Native as it no longer was a right fit for them:

Because we weren’t able to achieve our specific goals, we have decided that React Native isn’t right for us anymore.

This doesn’t mean they consider React Native to have failed, or that they dislike it:

63% of engineers would have chosen React Native again given the chance and 74% would consider React Native for a new project.

Be sure to read all parts and take your time for it. Lots of information. Lots of nuances. Your mileage may will vary.

React Native at Airbnb →

👨‍💻 Having worked on a *huge* React Native project for De Persgroep (video below) for the past 10 months, most of the things – both bad and good – Gabriel writes sound really familiar.

Unlike Airbnb though, we were fortunate enough to be creating a new app from scratch (instead of plugging RN onto an existing app), and got in to RN way past its pioneering phase (we started at RN 0.47) by which most wrinkles had already been ironed out.

Elsewhere , , Leave a comment

How Apple can fix 3D Touch

Eliz Kılıç writes down something that’s been bothering me too:

Apple introduced 3D Touch and its new related interactions Peek and Pop in 2014. It’s been almost 4 years since its first introduction, yet people don’t know/use 3D Touch. Why would they? Even tech-savvy users don’t know which buttons offer 3D touch. Let alone regular users.

The addition of a simple visual indicator could fix this …

How Apple can fix 3D Touch →

Elsewhere , , , Leave a comment

Animate CSS Grid Layouts with animate-css-grid

In a new(ish) web-project I’m working on, I went all-in on CSS Grid. The website contains a page where one can filter the list of elements shown. To animate this filtering I used animate-css-grid, as other libraries typically used for this – e.g. Isotope – don’t play nice with CSS Grid.

Installation per NPM/Yarn:

yarn add animate-css-grid

Once imported use the wrapGrid function to let the library work its magic (using MutationObserver and FLIP animations)

import { wrapGrid } from animateCSSGrid

const grid = document.querySelector(".grid");

animate-css-grid – Easy transitions for CSS Grid →

Elsewhere , , , , Leave a comment

Switching PHP versions with Laravel Valet

For some older projects that I still need to run, I recently started using Valet. As those projects sometimes require different versions of PHP – or when I want to test them with the latest PHP version – I followed this set of instructions by Michael Dyrynda:

A writeup is also available.

Switching PHP versions with Laravel Valet →

(via Freek)

Elsewhere , , Leave a comment

Siema – Lightweight and simple carousel with no dependencies

Siema is a lightweight (only 3kb gzipped) carousel plugin with no dependencies and no styling.It is 100% open source and available on Github. It is free to use on personal and commercial projects. Use it with your favourite module bundler or by manually injecting the script into your project.

I especially like the fact that it’s standalone and can easily be implemented into any existing code. The options you can pass into it (shown below), and API it exposes are exactly those I’d expect from such a library.

new Siema({
  selector: '.siema',
  duration: 200,
  easing: 'ease-out',
  perPage: 1,
  startIndex: 0,
  draggable: true,
  multipleDrag: true,
  threshold: 20,
  loop: false,
  rtl: false,
  onInit: () => {},
  onChange: () => {},

Installation per yarn/npm

yarn add siema

Siema – Lightweight and simple carousel with no dependencies →

Elsewhere , , Leave a comment

Sticky Events – Events for position: sticky;

Sticky Events is a library that can listen for events on elements that have position: sticky; applied. It’s an abstraction built on top of the IntersectionObserver, and provides one with three types of events:

  • StickyEvent.CHANGE: Fired when an element becomes stuck or unstuck
  • StickyEvent.STUCK: Fired only when an element becomes stuck
  • StickyEvent.UNSTUCK: Fired only when an element becomes unstuck

Usage is quite simple: set up it once, and then add event listeners to the elements:

import { observeStickyEvents, StickyEvent } from "sticky-events";

// Add listeners to all `.sticky-events` elements on the page

// Events are dispatched on elements with the `.sticky-events` class
const stickies = Array.from(document.querySelectorAll('.sticky-events'));

stickies.forEach((sticky) => {
  sticky.addEventListener(StickyEvent.CHANGE, (event) => {
    sticky.classList.toggle('bg-dark', event.detail.isSticky);

  sticky.addEventListener(StickyEvent.STUCK, (event) => {

  sticky.addEventListener(StickyEvent.UNSTUCK, (event) => {

Sticky Events – Events for position: sticky;

Elsewhere , , , Leave a comment

Responsive UIs in React Native

On the web it is – by now – obvious that you make your website responsive (*). Using a listener listening to the change event of the Dimensions API, it’s also possible to implement this kind of behaviour in React Native.

import {Component} from "react";
import {Dimensions} from "react-native";

export default class LogDimensionChanges extends Component {
    state = Dimensions.get("window");
    handler = dims => this.setState(dims);

    componentWillMount() {
        Dimensions.addEventListener("change", this.handler);

    componentWillUnmount() {
      // Important to stop updating state after unmount
      Dimensions.removeEventListener("change", this.handler);

    render() {
        const {width, height} = this.state.window;
        const mode = height > width ? "portrait" : "landscape";
        console.log(`New dimensions ${width}x${height} (${mode})`);
        return null;

This logic is applied into the react-native-responsive-ui package, which provides you with a <MediaQuery /> component, ResponsiveStyleSheet class, etc.:

import React, {Component} from "react";
import {View} from "react-native";
import {MediaQuery} from "react-native-responsive-ui";

export default class Login extends Component {
    render(): React$Element<*> {
        return <View>
            <MediaQuery minHeight={450} orientation="portrait">
                <Logo />

Responsive UIs in React Native →

(*) I know, this site still uses a non-responsive WordPress theme … shame on me

Elsewhere , , , Leave a comment

Dynamic Bézier Curves

Nice writeup by Josh Comeau on how he created the on-scroll-animated bézier curve on his website

Did you notice that as you started scrolling on this page, the Bézier curves that border the green title hero thingy started flattening? Keep your eye on the swoopy curves just above the post text as you scroll through the top of the document. Notice how they become flat as they approach the header at the top of the viewport?

In a delightful bit of serendipity, I realized while building the blog that this feature would make a great first blog post!

Love the interactive examples embedded into the post. They really help convey the message.

Dynamic Bézier Curves →

Elsewhere , , , Leave a comment