Hello.

I am Paul Kinlan.

A Developer Advocate for Chrome and the Open Web at Google.

Creating a commit with multiple files to Github with JS on the web

Paul Kinlan

Mon site est entirely static . Il est construit avec Hugo et hébergé avec Zeit . Je suis assez content de la configuration, je reçois des versions quasi instantanées et une livraison de contenu CDN très rapide et je peux faire tout ce dont j'ai besoin car je ne dois gérer aucun état. J'ai créé un simple UI pour ce site ainsi que mon podcast creator qui me permet de publier rapidement un nouveau contenu sur mon site hébergé de manière statique.

Read More

Paul Kinlan

Trying to make the web and developers better.

RSS Github Medium

Screen Recorder: recording microphone and the desktop audio at the same time

Paul Kinlan

Mon objectif est de créer le logiciel de capture d'écran le plus simple au monde et je m'occupe du projet lentement au cours des deux derniers mois (je veux dire très lentement).

Dans les articles précédents, j'avais eu le screen recording and a voice overlay en utilisant les flux de toutes les sources d'entrée. Un point de frustration cependant était que je ne pouvais pas savoir comment obtenir le son du bureau * et * superposer le son du haut-parleur. J'ai finalement trouvé comment le faire.

Tout d’abord, getDisplayMedia dans Chrome permet désormais la capture audio. Il semble qu’il getDisplayMedia un oubli dans Spec, dans la mesure où il ne vous permettait pas de spécifier audio: true dans l’appel de la fonction, c’est maintenant possible.

const audio = audioToggle.checked || false;
desktopStream = await navigator.mediaDevices.getDisplayMedia({ video:true, audio: audio });

Deuxièmement, j'avais initialement pensé qu'en créant deux pistes dans le flux audio, je pouvais obtenir ce que je voulais. Cependant, j'ai appris que l'API MediaRecorder de Chrome ne peut générer qu'une seule piste et, 2e, cela n'aurait pas fonctionné de toute façon, car les pistes sont comme les pistes audio multiples DVD en ce qu'un seul peut jouer à la fois.

La solution est probablement simple pour beaucoup de gens, mais elle était nouvelle pour moi: Utiliser Web Audio.

Il s'avère que les API WebAudio ont createMediaStreamSource et createMediaStreamDestination , deux API nécessaires à la résolution du problème. Le createMediaStreamSource peut prendre des flux de l'audio et du microphone de mon ordinateur. En reliant les deux ensemble à l'objet créé par createMediaStreamDestination il me permet de createMediaStreamDestination ce flux vers l'API MediaRecorder .

const mergeAudioStreams = (desktopStream, voiceStream) => {
  const context = new AudioContext();
    
  // Create a couple of sources
  const source1 = context.createMediaStreamSource(desktopStream);
  const source2 = context.createMediaStreamSource(voiceStream);
  const destination = context.createMediaStreamDestination();
  
  const desktopGain = context.createGain();
  const voiceGain = context.createGain();
    
  desktopGain.gain.value = 0.7;
  voiceGain.gain.value = 0.7;
   
  source1.connect(desktopGain).connect(destination);
  // Connect source2
  source2.connect(voiceGain).connect(destination);
    
  return destination.stream.getAudioTracks();
};

Simples.

Le code complet peut être trouvé sur my glitch , et la démo peut être trouvée ici: https://screen-record-voice.glitch.me/

{{<fast-youtube oGIdqcMFKlA>}}

Extracting text from an image: Experiments with Shape Detection

Paul Kinlan

J'ai eu un peu de temps après Google IO et je voulais me débarrasser de mes démangeaisons à long terme. Je veux juste pouvoir copier du texte contenu dans des images du navigateur. C'est tout. Je pense que ce serait une fonctionnalité intéressante pour tout le monde.

Il n'est pas facile d'ajouter des fonctionnalités directement dans Chrome, mais je sais que je peux tirer parti du système d'intention sur Android et que je peux maintenant le faire avec le Web (ou au moins Chrome sur Android).

Deux nouveaux ajouts à la plate-forme Web - Partage de niveau cible 2 (ou comme je l’appelle partage de fichiers) et TextDetector dans l’API de détection de forme - have allowed me to build a utility that I can Share images to and get the text held inside them .

L'implémentation de base est relativement simple: vous créez une cible de partage et un gestionnaire dans le Service Worker, puis une fois que vous avez l'image que l'utilisateur a partagée, vous exécutez TextDetector dessus.

Share Target API permet à votre application Web de faire partie du sous-système de partage natif. Dans ce cas, vous pouvez maintenant vous enregistrer pour gérer tous les types image/* en le déclarant comme suit dans votre Web App Manifest .

"share_target": {
  "action": "/index.html",
  "method": "POST",
  "enctype": "multipart/form-data",
  "params": {
    "files": [
      {
        "name": "file",
        "accept": ["image/*"]
      }
    ]
  }
}

Lorsque votre PWA est installé, vous le verrez dans tous les endroits où vous partagez des images, comme suit:

L'API Share Target traite le partage de fichiers comme une publication de formulaire. Lorsque le fichier est partagé avec Web App, l'agent de service est activé, le gestionnaire fetch est fetch avec les données du fichier. Les données se trouvent maintenant dans Service Worker, mais j'en ai besoin dans la fenêtre actuelle pour pouvoir les traiter. Le service sait quelle fenêtre a appelé la demande. Vous pouvez ainsi facilement cibler le client et lui envoyer les données.

self.addEventListener('fetch', event => {
  if (event.request.method === 'POST') {
    event.respondWith(Response.redirect('/index.html'));
    event.waitUntil(async function () {
      const data = await event.request.formData();
      const client = await self.clients.get(event.resultingClientId || event.clientId);
      const file = data.get('file');
      client.postMessage({ file, action: 'load-image' });
    }());
    
    return;
  }
  ...
  ...
}

Une fois l'image dans l'interface utilisateur, je la traite ensuite avec l'API de détection de texte.

navigator.serviceWorker.onmessage = (event) => {  
  const file = event.data.file;
  const imgEl = document.getElementById('img');
  const outputEl = document.getElementById('output');
  const objUrl = URL.createObjectURL(file);
  imgEl.src = objUrl;
  imgEl.onload = () => {
    const texts = await textDetector.detect(imgEl);
    texts.forEach(text => {
      const textEl = document.createElement('p');
      textEl.textContent = text.rawValue;
      outputEl.appendChild(textEl);
    });
  };
  ...
};

Le plus gros problème est que le navigateur ne fait pas naturellement pivoter l'image (comme vous pouvez le voir ci-dessous), et l'API de détection de la forme a besoin que le texte soit dans le sens de la lecture.

J'ai utilisé EXIF-Js library assez facile à utiliser, pour détecter la rotation, puis pour effectuer quelques manipulations de base sur la toile afin de réorienter l'image.

EXIF.getData(imgEl, async function() {
  // http://sylvana.net/jpegcrop/exif_orientation.html
  const orientation = EXIF.getTag(this, 'Orientation');
  const [width, height] = (orientation > 4) 
                  ? [ imgEl.naturalWidth, imgEl.naturalHeight ]
                  : [ imgEl.naturalHeight, imgEl.naturalWidth ];

  canvas.width = width;
  canvas.height = height;
  const context = canvas.getContext('2d');
  // We have to get the correct orientation for the image
  // See also https://stackoverflow.com/questions/20600800/js-client-side-exif-orientation-rotate-and-mirror-jpeg-images
  switch(orientation) {
    case 2: context.transform(-1, 0, 0, 1, width, 0); break;
    case 3: context.transform(-1, 0, 0, -1, width, height); break;
    case 4: context.transform(1, 0, 0, -1, 0, height); break;
    case 5: context.transform(0, 1, 1, 0, 0, 0); break;
    case 6: context.transform(0, 1, -1, 0, height, 0); break;
    case 7: context.transform(0, -1, -1, 0, height, width); break;
    case 8: context.transform(0, -1, 1, 0, 0, width); break;
  }
  context.drawImage(imgEl, 0, 0);
}

Et voila, si vous partagez une image avec l'application, celle-ci la fera pivoter puis analysera en renvoyant la sortie du texte qu'elle aura trouvé.

C'était incroyablement amusant de créer cette petite expérience et elle m'a été immédiatement utile. Il met toutefois en évidence les inconsistency of the web platform . Ces API ne sont pas disponibles dans tous les navigateurs. Elles ne sont même pas disponibles dans toutes les versions de Chrome. Cela signifie qu'en écrivant cet article Chrome OS, je ne peux pas utiliser l'application, mais en même temps, quand je peux l'utiliser. … OMG, tellement cool.

Wood Carving found in Engakuji Shrine near Kamakura

Sakura

Debugging Web Pages on the Nokia 8110 with KaiOS using Chrome OS

Paul Kinlan

Cet article est une continuation de l'article sur le débogage de KaiOS device with Web IDE , mais au lieu d'utiliser macOS, vous pouvez maintenant utiliser Chrome OS (m75) avec Crostini. Je viens de KaiOS Environment Setup ce qui est un bon début, mais pas suffisant pour KaiOS Environment Setup Chrome OS et Crostini. Ci-dessous le guide approximatif que j'ai suivi. Assurez-vous que vous utilisez au moins Chrome OS m75 (canal de développement actuel à compter du 15 avril), puis:

Read More

New WebKit Features in Safari 12.1 | WebKit

Paul Kinlan

Big mises à jour pour le dernier Safari!

Je pensais que c'était une annonce assez énorme, et le contraire de Google qui disait il y a quelque temps que Google Pay Lib était la méthode recommandée pour mettre en œuvre les paiements … Je veux dire, ce n'est pas à des millions de kilomètres, Google Pay est construit sur le dessus de demande de paiement, mais ce n'est pas d'abord le RP.

Payment Request is now the recommended way to pay implement Apple Pay on the web.

Read full post .

Et ma fonctionnalité préférée étant donné mon histoire avec Web Intents.

Web Share API

The Web Share API adds navigator.share(), a promise-based API developers can use to invoke a native sharing dialog provided the host operating system. This allows users to share text, links, and other content to an arbitrary destination of their choice, such as apps or contacts.

Maintenant, juste pour obtenir l'API Partager la cible et nous sommes sur un gagnant! :)

Offline fallback page with service worker

Paul Kinlan

Il y a des années, j'ai effectué des recherches sur la manière dont les applications natives répondaient au manque de connectivité réseau. Bien que j'ai perdu le lien avec l'analyse (je pourrais jurer que c'était sur Google+), le récit général était que de nombreuses applications natives sont inextricablement liées à Internet et refusent tout simplement de fonctionner. Cela ressemble à de nombreuses applications Web. Ce qui les différencie du Web, c’est que l’expérience était encore «sur la marque». Bart Simpson vous dirait que vous devez être en ligne (par exemple), et pourtant Dans la grande majorité des expériences Web, vous obtenez un 'Dino' (voir chrome: // dino).

Nous travaillons depuis longtemps sur Service Worker, et même si nous constatons que de plus en plus de sites ont des pages contrôlées par un technicien, la grande majorité des sites n’a même pas une expérience de repli de base lorsque le réseau n’est pas disponible. disponible.

J'ai demandé à mon bon ami Jake si nous avions des idées sur la façon de créer une page générique de repli, en partant du principe que vous ne souhaitiez pas créer une première expérience totalement hors ligne, et en 10 minutes, il l'avait créée. Check it out .

Par souci de brièveté, j'ai collé le code ci-dessous car il ne compte que 20 lignes environ. Il met en cache les ressources hors ligne. Ensuite, pour chaque extraction qui est une extraction de «navigation», il verra s'il y a une erreur (à cause du réseau), puis restituera la page hors connexion à la place du contenu d'origine.

addEventListener('install', (event) => {
  event.waitUntil(async function() {
    const cache = await caches.open('static-v1');
    await cache.addAll(['offline.html', 'styles.css']);
  }());
});

// See https://developers.google.com/web/updates/2017/02/navigation-preload#activating_navigation_preload
addEventListener('activate', event => {
  event.waitUntil(async function() {
    // Feature-detect
    if (self.registration.navigationPreload) {
      // Enable navigation preloads!
      await self.registration.navigationPreload.enable();
    }
  }());
});

addEventListener('fetch', (event) => {
  const { request } = event;

  // Always bypass for range requests, due to browser bugs
  if (request.headers.has('range')) return;
  event.respondWith(async function() {
    // Try to get from the cache:
    const cachedResponse = await caches.match(request);
    if (cachedResponse) return cachedResponse;

    try {
      // See https://developers.google.com/web/updates/2017/02/navigation-preload#using_the_preloaded_response
      const response = await event.preloadResponse;
      if (response) return response;

      // Otherwise, get from the network
      return await fetch(request);
    } catch (err) {
      // If this was a navigation, show the offline page:
      if (request.mode === 'navigate') {
        return caches.match('offline.html');
      }

      // Otherwise throw
      throw err;
    }
  }());
});

C'est tout. Lorsque l'utilisateur est en ligne, il verra l'expérience par défaut.

Et lorsque l'utilisateur est hors ligne, il obtiendra la page de secours.

Je trouve ce script simple incroyablement puissant et, bien qu’il puisse encore être amélioré, j’estime que même un simple changement dans la manière dont nous parlons avec nos utilisateurs en cas de problème avec le réseau a la capacité de s’améliorer fondamentalement. la perception du Web pour les utilisateurs du monde entier.

** Mise à jour ** Jeffrey Posnick kinldy m'a rappelé l'utilisation du préchargement de navigation afin de ne pas attendre le démarrage de SW pour toutes les demandes. Cela est particulièrement important si vous ne contrôlez que les demandes réseau échouées.

testing block image upload

Paul Kinlan

Ceci est juste un test pour voir si j'ai bien téléchargé l'image. Si vous voyez cela, alors oui je l'ai fait :)

Read More

Editor.js

Paul Kinlan

J'ai mis à jour par l'éditeur basé sur Hugo pour essayer d'utiliser EditorJS en tant qu'éditeur pour le blog.

Workspace in classic editors is made of a single contenteditable element, used to create different HTML markups. Editor.js workspace consists of separate Blocks: paragraphs, headings, images, lists, quotes, etc. Each of them is an independent contenteditable element (or more complex structure) provided by Plugin and united by Editor’s Core.

Read full post .

Je pense que ça marche.

J'ai eu un peu de mal avec le code base, les exemples utilisent tous des modules ES, cependant la distribution NPM est tout sortie en code IIFE ES5. Mais une fois que j'ai surmonté cet obstacle, il a été assez facile de créer une interface utilisateur qui ressemble un peu plus à un support.

Debugging Web Pages on the Nokia 8110 with KaiOS

Paul Kinlan

Nous avons récemment fait beaucoup de développement sur les téléphones polyvalents et cela a été difficile, mais amusant. Le problème le plus difficile est que, sur KaiOS, nous avons trouvé qu’il était impossible de déboguer des pages Web, en particulier sur le matériel dont nous disposions (le Nokia 8110). Le Nokia est un excellent appareil. Il est construit avec KaiOS. Il est basé sur quelque chose qui ressemble à Firefox 48, mais il est verrouillé, il n’existe pas de mode développeur traditionnel comme sur les autres appareils Android, ce qui signifie que vous ne pouvez pas connecter le navigateur de Firefox.

Read More

Object Detection and Augmentation

Paul Kinlan

J'ai beaucoup joué avec Shape Detection API dans Chrome et j'aime beaucoup son potentiel. Par exemple, un QRCode detector très simple QRCode detector j'ai écrit il y a longtemps possède un polyfill JS, mais utilise l'API new BarcodeDetector() si elle est disponible.

Vous pouvez voir certaines des autres démos que j'ai construites ici en utilisant les autres fonctionnalités de l'API de détection de forme: Face Detection , Barcode Detection et Text Detection .

J'ai été agréablement surpris lorsque je suis tombé sur Jeeliz le week-end et j'ai été extrêmement impressionné par les performances de leur boîte à outils - sachant que j'utilisais un Pixel3 XL, mais la détection des visages semblait nettement plus rapide que ce qui est possible avec l'API FaceDetector .

Checkout some of their demos .

Cela m'a beaucoup fait réfléchir. Cette boîte à outils pour la détection d'objets (et d'autres outils similaires) utilise des API largement disponibles sur le Web, notamment l'accès aux caméras, WebGL et WASM, contrairement à l'API de détection des formes de Chrome (disponible uniquement dans Chrome et non cohérente sur toutes les plateformes sur lesquelles Chrome est installé). ) peuvent être utilisés pour créer facilement des expériences riches et atteindre des milliards d'utilisateurs avec une expérience cohérente sur toutes les plateformes.

L'augmentation est l'endroit où cela devient intéressant (et vraiment ce que je voulais montrer dans cet article) et où vous avez besoin de bibliothèques de middlewares qui arrivent maintenant sur la plate-forme, nous pouvons créer des applications amusantes de filtrage des visages de Snapchat sans que les utilisateurs installent des applications MASSIVE. qui recueille une énorme quantité de données sur le périphérique de l'utilisateur (car il n'y a pas d'accès sous-jacent au système).

En dehors des démonstrations amusantes, il est possible de résoudre des cas d'utilisation très avancés rapidement et simplement pour l'utilisateur, tels que:

  • Sélection de texte directement à partir de l'appareil photo ou photo de l'utilisateur
  • Traduction en direct des langues de la caméra
  • Détection de QRCode en ligne pour que les gens n'aient pas à ouvrir WeChat tout le temps :)
  • Extraire automatiquement les URL du site Web ou l'adresse d'une image
  • Détection de carte de crédit et extraction de numéro (permet aux utilisateurs de s'inscrire rapidement sur votre site)
  • Recherche de produit visuelle dans l'application Web de votre magasin.
  • Recherche de code à barres pour plus de détails sur les produits dans l'application Web de votre magasin.
  • Recadrage rapide des photos de profil sur les visages des personnes.
  • Fonctions A11Y simples permettant à l’utilisateur d’entendre le texte contenu dans les images.

Je viens de passer 5 minutes à réfléchir à ces cas d'utilisation - je sais qu'il y en a beaucoup plus - mais je me suis rendu compte que nous ne voyons pas beaucoup de sites ou d'applications Web utiliser la caméra, mais que beaucoup de sites demandent à leur les utilisateurs à télécharger une application, et je ne pense pas que nous devions le faire plus.

** Mise à jour ** Thomas Steiner de notre équipe a mentionné dans notre équipe Chat qu'il ShapeDetection que je ShapeDetection API ShapeDetection actuelle. J'aime le fait que cette API nous donne accès aux implémentations d'expédition natives de chacun des systèmes respectifs. Cependant, comme je l'ai écrit dans The Lumpy Web , les développeurs Web recherchent la cohérence dans la plate-forme et de nombreux problèmes liés à l'API de détection de forme peuvent se résumer comme suit:

  1. L'API est uniquement dans Chrome
  2. L'API dans Chrome est très différente sur chaque plate-forme, car ses implémentations sous-jacentes sont différentes. Android n'a que des points pour des repères tels que la bouche et les yeux, où macOS a des contours. Sur Android, TextDetector renvoie le texte détecté, tandis que sur macOS, il renvoie un indicateur de «présence de texte» … Sans parler de tous les bogues trouvés par Surma.

Le Web, en tant que plate-forme de distribution, a tellement de sens pour des expériences de ce type que je pense que ce serait une négligence de notre part de ne pas le faire, mais les deux groupes de problèmes ci-dessus me poussent à remettre en question le besoin à long terme de mettre en œuvre toutes les fonctionnalités de la plate-forme Web en mode natif, lorsque nous pouvions implémenter de bonnes solutions dans un package livré avec les fonctionnalités de la plate-forme telles que WebGL, WASM et, à l'avenir, le processeur graphique Web.

Quoi qu'il en soit, j'aime le fait que nous puissions le faire sur le Web et je suis impatient de voir les sites expédiés avec eux.

Got web performance problems? Just wait...

Paul Kinlan

J'ai vu un tweet d'un bon Mariko et de Mariko collègue, Mariko , parler de tests sur une gamme de périphériques bas de gamme vous permettant de rester à la terre.

Le contexte du tweet est que nous examinons à quoi ressemble le développement Web lors de la création pour les utilisateurs qui vivent quotidiennement sur ces classes d’appareils.

L’équipe travaille actuellement beaucoup dans cet espace, mais j’ai passé une journée à construire un site et c’était incroyablement difficile de faire fonctionner quelque chose à un niveau de performances même légèrement raisonnable - voici certains des problèmes que j’ai rencontrés:

  • Bizarreries dans la fenêtre et réintroduction mystérieuse de 300 ms de délai de clic (possibilité de contourner le problème).
  • Reposes énormes de tout l'écran, et c'est lent.
  • Le réseau est lent
  • La mémoire est contrainte et les GC suivants verrouillent le thread principal pendant plusieurs secondes.
  • Exécution JS incroyablement lente
  • La manipulation du DOM est lente

Pour la plupart des pages que je construisais, même sur une connexion Wi-Fi rapide, le chargement des pages prenait plusieurs secondes et les interactions suivantes étaient tout simplement lentes. C’était difficile, cela impliquait d’essayer d’obtenir le plus possible le fil conducteur, mais c’était aussi incroyablement gratifiant, au niveau technique, de voir des changements dans les algorithmes et la logique que je n’aurais pas faits pour tout mon développement Web traditionnel. améliorations importantes des performances.

Je ne sais pas trop quoi faire à long terme, je soupçonne qu'un grand nombre de développeurs avec lesquels nous travaillons sur des marchés plus développés vont réagir: «Je ne construis pas de sites pour les utilisateurs dans [insérer le pays x]». haut niveau, il est difficile de discuter de cette affirmation, mais je ne peux pas ignorer le fait que 10 millions de nouveaux utilisateurs arrivent chaque année en informatique, qu'ils utiliseront ces appareils et que nous voulons que le Web soit * la * plate-forme de choix pour le contenu et les applications de peur que nous sommes heureux avec le rise of the meta platform .

Nous allons devoir continuer à améliorer la performance pendant encore longtemps. Nous allons continuer à créer des outils et des conseils pour aider les développeurs à charger rapidement et à avoir des interfaces utilisateur fluides :)

Browser Bug Searcher

Paul Kinlan

Je réfléchissais à quelques-uns des work our team has done et j'ai trouvé un projet de 2017 créé par Robert Nyman et Eric Bidelman. Browser Bug Searcher! .

Il est incroyable qu’avec quelques touches seulement vous ayez un bon aperçu de vos fonctionnalités préférées dans tous les principaux moteurs de navigateur.

Source code available .

Cela met en évidence l'un des problèmes que j'ai avec les outils de suivi des bogues de crbug et webkit: ils ne disposent pas d'un moyen simple d'obtenir des flux de données dans des formats tels que RSS. J'aimerais pouvoir utiliser mon agrégateur topicdeck avec des catégories de bogues, etc., de sorte que je dispose d'un tableau de bord de toutes les choses qui m'intéressent et qui est basé sur les dernières informations de chacun des suiveurs de bogues.

Github's Web Components

Paul Kinlan

Je cherchais un éditeur de https://www.webcomponents.org/ rapide sur https://www.webcomponents.org/ pour pouvoir publier facilement sur ce blog et je suis tombé sur un ensemble de composants github de github .

Je savais qu'ils avaient le <time-element> mais je ne savais pas qu'ils avaient un ensemble d'éléments aussi utiles et agréables.

London from Kingscross

The GDPR mess

Paul Kinlan

La manière dont nous (en tant qu'industrie) appliquons le consentement GDPR est un gâchis. Je ne suis pas sûr de savoir pourquoi quiconque choisirait autre chose que "Utiliser uniquement les cookies nécessaires", mais je ne peux vraiment pas faire la différence entre l'une ou l'autre option et le compromis de l'un ou l'autre choix, sans compter que je peux vérifier que c'est seulement en utilisant uniquement les cookies nécessaires.

Read More

Brexit: History will judge us all

Paul Kinlan

L’histoire nous jugera tous sur ce gâchis et j’espère que ce sera une étude de cas pour tous sur les effets du nationalisme, des intérêts personnels, de l’orgueil colonial, de la célébrité-bouffonnerie.

Fuckers

File Web Share Target

Paul Kinlan

J'ai souvent dit que pour que les applications Web soient concurrentielles dans le monde des applications, elles doivent être intégrées à tous les endroits que les utilisateurs attendent des applications. La communication inter-applications est l'un des principaux éléments manquants de la plate-forme Web, et l'une des dernières fonctionnalités manquantes est le partage au niveau natif: les applications Web doivent pouvoir accéder à data out of their silo et à d'autres sites Web et applications; ils doivent également pouvoir recevoir les données d'autres applications et sites natifs.

Read More

Testing-file-share-target-from-camera

Paul Kinlan

Ceci teste le partage directement à partir de l'application appareil photo. On dirait que ça a marché :)

Read More