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

Mi sitio es entirely static . Está construido con Hugo y alojado con Zeit . Estoy bastante contento con la configuración, obtengo construcciones casi instantáneas y entrega de contenido CDN súper rápido y puedo hacer todas las cosas que necesito porque no tengo que administrar ningún estado. He creado un simple UI para este sitio y también mi podcast creator que me permite publicar rápidamente contenido nuevo en mi sitio alojado de forma estática.

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

Tengo el objetivo de crear el software de grabación de pantalla más simple del mundo y he estado estudiando poco a poco el proyecto durante los últimos meses (me refiero a muy lentamente).

En publicaciones anteriores obtuve screen recording and a voice overlay al analizar con las secuencias de todas las fuentes de entrada. Sin embargo, un área de frustración fue que no pude averiguar cómo obtener el audio del escritorio * y * superponer el audio del altavoz. Finalmente me di cuenta de cómo hacerlo.

En primer lugar, getDisplayMedia en Chrome ahora permite la captura de audio, parece que hay un descuido extraño en la especificación porque no le permitió especificar audio: true en la llamada de función, ahora puede audio: true .

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

En segundo lugar, originalmente pensé que al crear dos pistas en el flujo de audio sería capaz de obtener lo que quería, sin embargo, aprendí que la API MediaRecorder de Chrome solo puede generar una pista y, en segundo lugar, no habría funcionado de todos modos porque Son como las pistas de audio de varios clips de DVD, ya que solo uno puede reproducir a la vez.

La solución es probablemente simple para mucha gente, pero era nueva para mí: usar audio web.

Resulta que la API de WebAudio tiene createMediaStreamSource y createMediaStreamDestination , que son las API necesarias para resolver el problema. createMediaStreamSource puede recibir transmisiones desde el audio y el micrófono de mi escritorio, y al conectar las dos juntas en el objeto creado por createMediaStreamDestination , me da la posibilidad de canalizar esta transmisión en la API de 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.

El código completo se puede encontrar en my glitch , y la demostración se puede encontrar aquí: https://screen-record-voice.glitch.me/

{{<fast-youtube oGIdqcMFKlA>}}

Extracting text from an image: Experiments with Shape Detection

Paul Kinlan

Tuve un poco de tiempo de inactividad después de Google IO y quise rascarme una picazón a largo plazo que he tenido. Solo quiero poder copiar el texto que se encuentra dentro de las imágenes en el navegador. Eso es todo. Creo que sería una buena característica para todos.

No es fácil agregar funcionalidad directamente a Chrome, pero sé que puedo aprovechar el sistema de intención en Android y ahora puedo hacerlo con la Web (o al menos Chrome en Android).

Dos nuevas adiciones a la plataforma web: Share Target Level 2 (o como me gusta llamarlo File Share) y TextDetector en la API de detección de formas - have allowed me to build a utility that I can Share images to and get the text held inside them .

La implementación básica es relativamente directa, crea un objetivo compartido y un manejador en el trabajador de servicio, y luego, una vez que tiene la imagen que el usuario ha compartido, ejecuta TextDetector en ella.

Share Target API permite que su aplicación web sea parte del subsistema de uso compartido nativo, y en este caso, ahora puede registrarse para manejar todos los tipos de image/* declarándolo dentro de Web App Manifest siguiente manera.

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

Cuando su PWA esté instalado, lo verá en todos los lugares desde donde comparte imágenes de la siguiente manera:

La API Share Target trata el intercambio de archivos como una publicación de formulario. Cuando el archivo se comparte con la aplicación web, el trabajador del servicio se activa, el controlador fetch se invoca con los datos del archivo. Los datos ahora están dentro del Trabajador de servicios, pero los necesito en la ventana actual para poder procesarlos, el servicio sabe qué ventana invocó la solicitud, para que pueda dirigirse fácilmente al cliente y enviarle los datos.

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;
  }
  ...
  ...
}

Una vez que la imagen está en la interfaz de usuario, la proceso con la API de detección de texto.

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);
    });
  };
  ...
};

El mayor problema es que el navegador no rota naturalmente la imagen (como se puede ver a continuación), y la API de detección de formas necesita que el texto esté en la orientación de lectura correcta.

Utilicé EXIF-Js library bastante fácil de usar, para detectar la rotación y luego realizar una manipulación básica del lienzo para reorientar la imagen.

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);
}

Y voila, si comparte una imagen con la aplicación, la girará y luego la analizará devolviendo la salida del texto que ha encontrado.

Fue increíblemente divertido crear este pequeño experimento, y ha sido inmediatamente útil para mí. Sin embargo, sí resalta las inconsistency of the web platform . Estas API no están disponibles en todos los navegadores, ni siquiera están disponibles en todas las versiones de Chrome. Esto significa que mientras escribo este artículo sobre Chrome OS, no puedo usar la aplicación, pero al mismo tiempo, cuando puedo usarla. … Dios mío, tan genial.

Wood Carving found in Engakuji Shrine near Kamakura

Sakura

Paul Kinlan

Me han dicho que, más concretamente, esto es 'Yaezakura'.

Read More

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

Paul Kinlan

Esta publicación es una continuación de la publicación sobre la depuración de KaiOS device with Web IDE , pero en lugar de usar macOS, ahora puede usar Chrome OS (m75) con Crostini. Estoy KaiOS Environment Setup que es un buen comienzo, pero no lo suficiente como para empezar a KaiOS Environment Setup Chrome OS y Crostini. A continuación se muestra la guía aproximada que seguí. Asegúrese de que está utilizando al menos Chrome OS m75 (actualmente, el canal dev es el 15 de abril), luego:

Read More

New WebKit Features in Safari 12.1 | WebKit

Paul Kinlan

Grandes actualizaciones para el último Safari!

Pensé que este era un anuncio bastante grande, y lo opuesto a Google, que hace un tiempo dijo que Google Pay Lib es la forma recomendada de implementar pagos … Quiero decir, no está a un millón de millas, Google Pay está construido en la parte superior. de solicitud de pago, pero no es PR primero.

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

Read full post .

Y mi característica favorita dada mi historia con Intentos Web.

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.

¡Ahora solo para obtener Share Target API y estamos ante un ganador! :)

Offline fallback page with service worker

Paul Kinlan

Hace años, hice una investigación sobre cómo las aplicaciones nativas respondían a una falta de conectividad de red. Si bien he perdido el enlace al análisis (podría jurar que estaba en Google+), la narrativa general fue que muchas aplicaciones nativas están inextricablemente ligadas a Internet y que simplemente se niegan a funcionar. Suena como una gran cantidad de aplicaciones web, aunque lo que los diferencia de la web es que la experiencia fue "de marca", Bart Simpson le diría que necesita estar en línea (por ejemplo), y aún así para la La gran mayoría de las experiencias web te dan un 'Dino' (ver chrome: // dino).

Hemos estado trabajando en Service Worker desde hace mucho tiempo, y mientras vemos que cada vez más sitios tienen páginas controladas por un Service Worker, la gran mayoría de los sitios ni siquiera tienen una experiencia de respaldo básica cuando la red no está disponible.

Le pregunté a mi buen amigo Jake si tenemos alguna duda sobre cómo construir una página de respaldo genérica suponiendo que no desea crear una experiencia totalmente fuera de línea, y en 10 minutos lo había creado. Check it out .

Por brevedad, he pegado el código a continuación porque solo tiene unas 20 líneas. Almacena en caché los recursos fuera de línea, y luego, por cada captura que es una búsqueda de 'navegación', verá si se produce un error (debido a la red) y luego representa la página fuera de línea en lugar del contenido original.

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;
    }
  }());
});

Eso es todo. Cuando el usuario está en línea, verá la experiencia predeterminada.

Y cuando el usuario está fuera de línea, obtendrán la página alternativa.

Creo que este simple script es increíblemente poderoso, y sí, aunque aún se puede mejorar, creo que incluso un simple cambio en la forma en que hablamos con nuestros usuarios cuando hay un problema con la red tiene la capacidad de mejorar fundamentalmente La percepción de la web para los usuarios de todo el mundo.

** Actualización ** Jeffrey Posnick kinldy me recordó sobre el uso de la precarga de navegación para no tener que esperar en el inicio del SW para todas las solicitudes, esto es especialmente importante si solo está controlando las solicitudes de red fallidas.

testing block image upload

Paul Kinlan

Esto es solo una prueba para ver si he subido correctamente la imagen. Si ves esto, entonces sí lo hice :)

Read More

Editor.js

Paul Kinlan

He actualizado el editor basado en Hugo para probar y usar EditorJS como, bueno, el editor para el 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 .

Creo que funciona.

Luché un poco con el código base, todos los ejemplos usan módulos ES, sin embargo, el NPM dist es todo en código IIFE ES5. Pero una vez que superé ese obstáculo, ha sido bastante fácil construir una interfaz de usuario que se parece un poco más al medio.

Debugging Web Pages on the Nokia 8110 with KaiOS

Paul Kinlan

Hemos estado haciendo mucho desarrollo en teléfonos con funciones recientemente y ha sido difícil, pero divertido. Lo más difícil es que en KaiOS fue imposible depurar las páginas web, especialmente en el hardware que teníamos (el Nokia 8110). El Nokia es un gran dispositivo, está construido con KaiOS, y sabemos que se basa en algo similar a Firefox 48, pero está bloqueado, no hay un modo de desarrollador tradicional como el que usas en otros dispositivos Android, lo que significa que no puedes conectar Firefox.

Read More

Object Detection and Augmentation

Paul Kinlan

He estado jugando mucho con Shape Detection API en Chrome y me gusta mucho el potencial que tiene, por ejemplo, un QRCode detector muy simple que escribí hace mucho tiempo tiene un polyfill JS, pero usa la API new BarcodeDetector() si está disponible.

Puede ver algunas de las otras demostraciones que he construido aquí utilizando las otras capacidades de la API de detección de formas: Face Detection , Barcode Detection y Text Detection .

Me sorprendió gratamente cuando me tropecé con Jeeliz el fin de semana y me impresionó mucho el rendimiento de su conjunto de herramientas, aunque estaba usando un Pixel3 XL, pero la detección de rostros parecía significativamente más rápida de lo que es posible con la API FaceDetector .

Checkout some of their demos .

Me hizo pensar mucho. Este kit de herramientas para la detección de objetos (y otros similares) utiliza API que están ampliamente disponibles en la web, específicamente el acceso a la cámara, WebGL y WASM, que a diferencia de la API de detección de formas de Chrome (que solo está en Chrome y no es consistente en todas las plataformas en las que Chrome está ) se puede utilizar para crear experiencias ricas fácilmente y llegar a miles de millones de usuarios con una experiencia consistente en todas las plataformas.

El aumento es donde se pone interesante (y realmente lo que quería mostrar en esta publicación) y donde necesita las bibliotecas de middleware que ahora están llegando a la plataforma, podemos crear las divertidas aplicaciones de filtro de caras de Snapchat sin que los usuarios instalen aplicaciones MASIVAS esa cosecha gran cantidad de datos del dispositivo de los usuarios (porque no hay acceso subyacente al sistema).

Fuera de las divertidas demostraciones, es posible resolver casos de uso muy avanzados de forma rápida y sencilla para el usuario, como:

  • Selección de texto directamente desde la cámara o foto del usuario.
  • Traducción en vivo de idiomas desde la cámara.
  • Detección de código QR en línea para que las personas no tengan que abrir WeChat todo el tiempo :)
  • Extraiga automáticamente las URL del sitio web o la dirección de una imagen
  • Detección de tarjeta de crédito y extracción de números (haga que los usuarios se registren más rápido en su sitio)
  • Búsqueda visual de productos en la aplicación web de tu tienda.
  • Búsqueda de códigos de barras para obtener más detalles del producto en la aplicación web de su tienda.
  • Recorte rápido de fotos de perfil en las caras de las personas.
  • Funciones simples de A11Y para que el usuario escuche el texto que se encuentra en las imágenes.

Acabo de pasar 5 minutos pensando en estos casos de uso, sé que hay muchos más, pero me parece que no vemos muchos sitios o aplicaciones web que utilizan la cámara, sino que vemos muchos sitios que preguntan a sus usuarios para descargar una aplicación, y no creo que tengamos que hacerlo más.

** Actualización ** Thomas Steiner en nuestro equipo mencionó en nuestro equipo Chat que parece que no me gusta la actual API ShapeDetection . Me encanta el hecho de que esta API nos da acceso a las implementaciones de envío nativas de cada uno de los sistemas respectivos. Sin embargo, como escribí en The Lumpy Web , los Desarrolladores Web anhelan consistencia en la plataforma y hay varios problemas con la API de Detección de Forma que pueden resumirse como:

  1. La API está solo en Chrome
  2. La API en Chrome es muy diferente en cada plataforma porque sus implementaciones subyacentes son diferentes. Android solo tiene puntos para puntos de referencia como boca y ojos, donde macOS tiene contornos. En Android, TextDetector devuelve el texto detectado, mientras que en macOS devuelve un indicador de 'Presencia de texto' … Esto es para no mencionar todos los errores que encontró Surma.

La web como plataforma para la distribución tiene tanto sentido para experiencias como estas que creo que sería negligente que no lo hagamos, pero los dos grupos de problemas anteriores me llevan a cuestionar la necesidad a largo plazo de implementar cada característica en la plataforma web de forma nativa, cuando podríamos implementar buenas soluciones en un paquete que se envía con las características de la plataforma actual como WebGL, WASM y en la futura GPU web.

De todos modos, me encanta el hecho de que podamos hacer esto en la web y espero ver los sitios que se envían con ellos.

Got web performance problems? Just wait...

Paul Kinlan

Vi un tweet de un buen amigo y colega, Mariko , acerca de las pruebas en una gama de dispositivos de gama baja que lo mantienen realmente conectado a tierra.

El contexto del tweet es que estamos analizando cómo es el desarrollo web cuando construimos para usuarios que viven diariamente en estas clases de dispositivos.

El equipo está haciendo mucho trabajo ahora en este espacio, pero pasé un día construyendo un sitio y fue increíblemente difícil hacer que algo funcionara a un nivel de rendimiento incluso ligeramente razonable. Estos son algunos de los problemas que encontré:

  • Las rarezas de la ventana gráfica, y la reintroducción misteriosa de 300 ms de retraso de clics (puede funcionar alrededor).
  • Grandes repintados de pantalla completa, y es lento.
  • La red es lenta
  • La memoria está restringida, y los GC subsiguientes bloquean el hilo principal por varios segundos
  • Ejecución increíblemente lenta de JS
  • La manipulación de DOM es lenta

Para muchas de las páginas que estaba creando, incluso en una conexión wifi rápida las páginas tardaron varios segundos en cargarse, y las interacciones posteriores fueron simplemente lentas. Fue difícil, involucró intentar alejarme lo más posible del hilo principal, pero también fue increíblemente gratificante a nivel técnico ver cambios en los algoritmos y la lógica que no habría hecho con todo mi desarrollo web tradicional. Grandes mejoras en el rendimiento.

No estoy seguro de qué hacer a largo plazo, sospecho que una gran cantidad de desarrolladores con los que trabajamos en los mercados más desarrollados tendrá una reacción "No estoy construyendo sitios para usuarios en [insertar país x]", y en un de alto nivel es difícil discutir esta afirmación, pero no puedo ignorar el hecho de que 10 de los millones de nuevos usuarios ingresan a la informática cada año y usarán estos dispositivos y queremos que la web sea * la * plataforma de elección para contenido y aplicaciones para que no estemos contentos con rise of the meta platform .

Vamos a tener que seguir presionando en el rendimiento durante un largo tiempo por venir. Seguiremos creando herramientas y guías para ayudar a los desarrolladores a cargar rápidamente y tener interfaces de usuario fluidas :)

Browser Bug Searcher

Paul Kinlan

Solo estaba reflexionando sobre algunos de los work our team has done y encontré un proyecto de 2017 que Robert Nyman y Eric Bidelman crearon. Browser Bug Searcher! .

Es increíble que con solo unas pocas pulsaciones de tecla tenga una gran visión general de sus funciones favoritas en todos los motores de navegación principales.

Source code available .

Esto realmente resalta uno de los problemas que tengo con los rastreadores de errores de crbug y webkit, no tienen una forma sencilla de recibir fuentes de datos en formatos como RSS. Me encantaría poder usar mi agregador topicdeck con categorías de errores, etc., así que tengo un panel de todas las cosas en las que estoy interesado en base a la información más reciente de cada uno de los rastreadores de errores.

Github's Web Components

Paul Kinlan

Estaba buscando un editor de rebajas rápido en https://www.webcomponents.org/ para poder facilitar la publicación en este blog y me encontré con un conjunto de componentes github por github .

Sabía que tenían las <time-element> pero no sabía que tenían un conjunto tan agradable y simple de elementos útiles.

London from Kingscross

The GDPR mess

Paul Kinlan

La forma en que nosotros (como industria) implementamos el consentimiento de GDPR es un desastre. No estoy seguro de por qué alguien elegiría otra cosa que no sea 'Usar solo las cookies necesarias', sin embargo, realmente no puedo distinguir entre una opción y la compensación de una u otra opción, por no mencionar que puedo verificar que es solo utilizando únicamente las cookies necesarias.

Read More

Brexit: History will judge us all

Paul Kinlan

La historia nos juzgará a todos en este lío, y espero que sea un estudio de caso para todos sobre los efectos del nacionalismo, los intereses personales, la arrogancia colonial, la bafonía de las celebridades.

Putos

File Web Share Target

Paul Kinlan

Con frecuencia he dicho que para que las aplicaciones web puedan competir de manera efectiva en el mundo de las aplicaciones, deben integrarse en todos los lugares en los que los usuarios esperan que estén. La comunicación entre aplicaciones es una de las piezas faltantes más importantes de la plataforma web, y específicamente una de las últimas características faltantes más importantes es el uso compartido a nivel nativo: las aplicaciones web deben poder obtener data out of their silo y otros sitios web y aplicaciones; también deben poder recibir los datos de otras aplicaciones y sitios nativos.

Read More

Testing-file-share-target-from-camera

Paul Kinlan

Esto es probar compartir directamente desde la aplicación de la cámara. Parece que funcionó :)

Read More