Nono.MA

NOVEMBER 9, 2023

After upgrading your Mac to macOS Sonoma, you may have encountered this issue with Canon's IJ Scan Utility2, a known issue up to version 2.4.0. In my case, I'm using the Canon CanoScan LiDE 220. But the same software works with LiDE 300 and 400.

An internal error has occurred. Take the following measures.
- Check the scanner status.
- Restart the scanner.
- Restart the computer, then try again.
- Reinstall the scanner driver.

Code:10,202,3

Canon CanoScan LiDE 220

You can Download the latest software on Canon's website—including the IJ Scan Utility2 2.4.1and the ICA Driver Ver.5.0.0—which add macOS Sonoma compatibility.

  • Go to Software & Drivers
  • Set the Operating System to Mac (detected)
  • Set the Version to macOS Sonoma v14
  • Download and install IJ Scan Utility2 Ver.2.4.1 (Mac) (25.42 MB)
  • Download and install ICA Driver Ver.5.0.0 (Mac) (4.71 MB)
  • Download and install ScanGear Starter EX Ver.1.2.0 (Mac) (5.47 MB)

Other scanners

OCTOBER 16, 2023

YouTube is forbidding ad blockers.

What does this mean for YouTube's usage?

Will YouTubers generate more revenue?

SEPTEMBER 29, 2023

I was running cron jobs that worked with macOS Mojave, Catalina, Big Sur, Monterey, and Ventura but stopped working after I updated to macOS Sonoma.

Here are two sample errors.

ls: .: Operation not permitted
zip error: Nothing to do! (try: zip -qr9 ~/folder/file.zip . -i *)

An "Operation not permitted" error message when running a cron job on macOS typically signals a permission issue.

Fix: Provide Full Disk Access to cron

cron requires the proper permissions to access other commands.

You'll need to grant "Full Disk Access" to cron or to the Terminal app to ensure it can execute jobs properly in macOS Sonoma.

Here's how.

  • Go to System Preferences > Security & Privacy > Privacy section.
  • Unlock the settings by clicking the padlock at the bottom and entering your password.
  • Select Full Disk Access from the sidebar.
  • Navigate to the /usr/sbin folder with Finder.
  • Drag the cron app to the list of allowed apps.

ChatGPT helped me get to a solution faster.

SEPTEMBER 27, 2023

Say we have a TypeScript interface with required and optional values.

interface NonosOptions {
  thickness: number,
  pressure?: number
}

thickness is required, pressure is optional.

If we create an object of type NonosOptions, we can omit pressure but not thickness.

const options: NonosOptions = {
  thickness: 1.5
}

We can now deconstruct our options with a default pressure value, which will only be used if options doesn't define a value.

const { thickness = 2, pressure = 0.75 } = options
// thickness = 1.5
// pressure = 0.75

As you can see, thickness ignores the 2 assignment because options sets it as 1.5. But pressure is set to 0.75 because options doesn't define a pressure value.

If pressure is defined in options, both thickness and pressure deconstruction fallback values would be ignored.

const options: NonosOptions = {
  thickness: 1.5,
  pressure: 0.25
}

const { thickness = 2, pressure = 0.75 } = options
// thickness = 1.5
// pressure = 0.25

JULY 21, 2023

Looking for products on Amazon is a loophole.

Amazon prices are competitive. They often match offers from other vendors automatically. In Spain, for instance, they match MediaMarkt's discounts to the cent.

The issue is that offers and discounts are not always real discounts. Amazon may have a 100-euro product listed at 60 euros (40% discount) when in reality, everyone's selling the product at that price now, and 100 euros was when the product was released. It's not a discount; it's the product's current price.

What I want to do is remember the different Amazon prices I saw when visiting the product or even adding it to the shopping cart.

The idea is to create a Google Chrome extension to track the price of an Amazon page when I visit it, creating a log of actual prices. That way, when you return, you can know if that fifty percent off is an actual sale.

JULY 20, 2023

If you're trying to run a Bash script and get a Permission Denied error, it's probably because you don't have the rights to execute it.

Let's check that's true.

# Get the current file permissions.
stat -f %A script.sh
# 644

With 644, the user owner can read and write but not execute.1

Set the permissions to 755 to fix the issue.

chmod 755 script.sh

  1. Chmod 644. CHMOD Calculator. 

JULY 19, 2023

Even though Vite doesn't like chunks larger than 500 kBs after minification, you can increase the kB limit. Remember, this is just a warning, not an error.

An alternative solution is to chunk your JavaScript bundle into separate chunks, known as chunking. You can do this with the vite-plugin-chunk-split package.

JUNE 2, 2023

I've been doing a lot of React and TypeScript work lately. It had been a few years since I worked with them on a daily basis, and things are improving a lot. It's a breeze to work with some of these technologies to build web apps, and one of the newest additions that works well is Vite.

Is anyone else working with React these days? I will cover some of my learnings on YouTube and want to get a sense of interest. (Let me know on Discord.)

What's cool is that frameworks such as ONNX and TensorFlow have wide support to export and run models in the browser (web workers, WebGPU, WebAssembly) and you don't even need to build microservices for certain models. (Plus there's now support for Node.js to run in the browser as well!)

MAY 31, 2023

I just started a new daily file with my &ndaily Typinator text expansion. This expansion archives my current daily file, an action I run whenever a daily file goes over seven thousand words. It then creates a new file named —01_Daily_Part_94.md for Daily 94.

A few weeks ago, I paid for a Typinator 9 upgrade. The app is more modern, has light and dark modes, and promises long-term support. I'm glad they did that.

I'm a heavy user of Typinator and, someday, I'll create a list of all the things I use on a daily basis.

One of my most-used expansions—they've added usage stats (!)—is dtt, which would expand, today, to 230531.

MAY 10, 2023

No matter how small a piece of software is, it requires maintenance.

Except when it doesn't, which is true for certain programs without external dependencies and deprecated features.

The more code bases you rely on or develop, the more tiny efforts you'll have to put here and there to keep them running, especially if you want to keep the operating system up to date.

APRIL 27, 2023

Live 100 Special: Creative AI with Friends

Hi Friends!

I'm hosting a live conversation today with special guests to celebrate my 100th YouTube live stream, Thursday, April 27, at 10:30 AM Pacific Time.

I've invited Adam Menges (ex-Lobe.ai), Joel Simon (Artbreeder), Jose Luis Garcia del Castillo (Harvard, ParametricCamp), and Kyle Steinfeld (University of California, Berkeley) to pick their brains on creative machine intelligence and how it's being used in academia and next-generation design tools.

The conversation will take place in Riverside at nono.ma/live/100.

With that link, you'll join as part of the audience and can participate in the chat. There's an option to "call in" and join the call, which we could use for questions or even to have everyone who wants to join at the end of the call.

Feel free to forward this invite to friends interested in AI & ML.

Thanks so much for being part of my journey.

Warmly,

Nono

Join the Live Event

APRIL 26, 2023

Alex O'Connor — Transformers, Generative AI, and the Deep Learning Revolution

Hi Friends—

Alex O'Connor is a researcher and machine learning manager.

I had the chance to pick his brain on the latest trends of generative AI — transformers, language and image models, fine-tuning, prompt engineering, tokenization, the latent space, adversarial attacks, and more.

Thanks to everyone who chatted with us during the YouTube premiere.

★ I'm excited to celebrate Live 100 with Special Guests tomorrow, April 27, at 1:30 PM ET with a conversation on creative machine intelligence with Adam Menges, Joel Simon, José Luis García del Castillo, and Kyle Steinfeld.

I'd love for you to join us live at nono.ma/live/100.

Warmly,
Nono



Alex O'Connor and Nono Martínez Alonso at Vegas.

Recorded at The Palazzo, Las Vegas on December 2022.

Chapters

00:00 · Introduction
00:40 · Machine learning
02:36 · Spam and scams
15:57 · Adversarial attacks
20:50 · Deep learning revolution
23:06 · Transformers
31:23 · Language models
37:09 · Zero-shot learning
42:16 · Prompt engineering
43:45 · Training costs and hardware
47:56 · Open contributions
51:26 · BERT and Stable Diffusion
54:42 · Tokenization
59:36 · Latent space
01:05:33 · Ethics
01:10:39 · Fine-tuning and pretrained models
01:18:43 · Textual inversion
01:22:46 · Dimensionality reduction
01:25:21 · Mission
01:27:34 · Advice for beginners
01:30:15 · Books and papers
01:34:17 · The lab notebook
01:44:57 · Thanks

APRIL 13, 2023

Here are three ways to define a React component in TypeScript which, in the end, are three ways to define a function in TypeScript—React components are JavaScript functions.

const MyComponent = (text: string) => <>{text}</>
const MyComponent = (text: string) => {
  return (
    <>{text}</>
  )
}
function MyComponent(text: string) {
  return <>{text}</>
}

APRIL 5, 2023

import * as React from 'react'
import * as Server from 'react-dom/server'

let Greet = () => <h1>Hello, Nono!</h1>
console.log(Server.renderToString(<div><Greet /></div>))
// <div><h1>Hello, Nono!</h1></div>

The tricky part is running this code.

You first need to build it, say, with esbuild, then execute it.

# Build with esbuild.
esbuild RenderToString.jsx --bundle --outfile=RenderToString.js

# Run with Node.js.
node RenderToString.js
# <div><h1>Hello, Nono!</h1></div>

APRIL 3, 2023

Here's how to deploy your Vite app to your local network so you can access it from other devices connected to the same WiFi. Say, your iPhone or iPad.

TL;DR

npx vite --host {local-ip-address}

If you're on macOS, you can simply run the following.

npx vite --host $(ipconfig getifaddr en0)

Overview

A fresh Vite project will likely have a dev key in your package.json's scripts property mapping that Yarn or NPM command to Vite, e.g., "dev": "vite" so you can type yarn dev or npm run dev and have Vite run your application in development mode.

yarn dev
#  VITE v4.2.1  ready in 165 ms
#
#  ➜  Local:   http://localhost:5173/
#  ➜  Network: use --host to expose
#  ➜  press h to show help

That's the same as running npx vite or ./node_modules/.bin/vite.

Figuring out your local IP address

Before we can deploy to our IP address, we need to know what it is.

You can use ipconfing on Windows and ifconfig on macOS.

Henry Black shared a trick to get your Mac's local IP address with ifconfig.

ipconfig getifaddr en0
# 192.168.1.34

Deploying to your local IP address

All you need to do is pass your IP address as Vite's --host argument.

npx vite --host $(ipconfig getifaddr en0)
#  VITE v4.2.1  ready in 166 ms
#
#  ➜  Local:   http://192.168.1.34:5173/
#  ➜  Network: http://192.168.1.34:5173/
#  ➜  press h to show help

Now I can access my Vite app from other devices in the same network, which comes in handy if you want to test your app on other computers, phones, or tablets.

Remember, npx vite is interchangeable with yarn dev, npm run dev, or ./node_modules/.bin/vite`.

For more information, read Vite's Server Options.

If you found this useful, let me know at @nonoesp!

MARCH 31, 2023

Here's how to connect and communicate with WebSocket servers from browser client applications using the WebSocket API and the WebSocket protocol.

// Create a WebSocket client in the browser.
const ws = new WebSocket("ws://localhost:1234");

// Log incoming messages to the console.
ws.onmessage = function (event) {
  // This runs when receiving message.
  console.log(event.data);
};

ws.onopen = () => {
  // This runs when we connect.
  // Submit a message to the server
  ws.send(`Hello, WebSocket! Sent from a browser client.`);
};

MARCH 30, 2023

Note that if you restart your Droplet you may have to restart services that are running in the background manually.

# Restart the Droplet now
shutdown -r now

In my case, if Nginx doesn't restart automatically after the restart, I need to run the following commands.

sudo fuser -k 80/tcp && sudo fuser -k 443/tcp
sudo service nginx restart

MARCH 29, 2023

Here's a one-liner to turn any website into dark mode.

body, img { filter: invert(0.92) }

I apply this to selected sites using Stylebot, a Chrome extension that lets you apply custom CSS to specific websites.

In a nutshell, the CSS inverts the entire website and then inverts images again to render them normally. You can adjust the invert filter's amount parameter, which in the example is set to 0.92. 0 would be no color inversion at all. 100 would be full-color inversion; whites turn black, and blacks turn white. I often prefer to stay within 90–95% to reduce the contrast.

MARCH 27, 2023

After two months of pause, we're preparing to release new Getting Simple podcast episodes.

Editing and publishing add friction and delays to my process, so I'm exploring code and ML workflows to post-process of episodes' audio and generate transcripts, summaries & notes.

I'm not there yet. But OpenAI's Whisper (free) and Descript (paid) already provide accurate transcriptions. Existing projects and companies use #GPT-like language models to extract episode keywords, topics, chapters & summaries.

We'll soon have automatic episode notes.

It's exciting. I think we're getting very, very close. I've also played with Spotify's pedalboard Python package to post-process audio without relying on a Digital Audio Workstation (DAW).

That's cool because I can create reusable scripts for specific recording conditions and forget about audio editing — say, compressing, limiting, applying noise gates, or normalization—things you'd otherwise do in Adobe Audition.

Let me know if you'd like to see these automations in the live stream and video tutorials or shared here on Twitter at @nonoesp.

MARCH 23, 2023

The point of writing as a human is to express ourselves. To pour words on paper (or the screen) and reflect on who you are, to learn, to evolve, and to inspire others. You can influence and inspire your future self as well.

Yesterday, I woke up and started the day writing five hundred words before I did any work. This is a practice I follow and will continue to follow. It doesn't make sense to delegate this to a machine because the whole point is to pour things out of my mind. Maybe this can turn into a conversation with an AI in the long run. I talk, we discuss, and my virtual assistant takes notes and generates a document instead of typing at my desk with a keyboard.

Machine intelligence is here to stay, and we'll find it harder to be original as it improves. But we must remember that they work because of all the knowledge humans have created before, with our mistakes and biases. Only they'll get better if we continue to produce original content. That may be a mistaken assumption, but I believe it in some way. AI originality is probably down the road, and current systems can hallucinate. But I like to think we'll do better work together with them. We must wait until everything is stable to identify which parts won't be done by humans anymore. Maybe they will but at a scary-fast pace.

Writing is a medium for creative expression, as are drawing, singing, film, photography, and many, many other forms. Get a pen and write—express yourself. Type with your fingers or thumbs. Shoot a video. Take a photo. Doodle. Tell us a story.

MARCH 22, 2023

I've installed vnstat on my M1 MacBook Pro with Homebrew to monitor my network usage over time.

# Install vnstat on macOS with Homebrew.
brew install vnstat

Make sure you start the vnstat service with brew for vnstat to monitor your network usage.

brew services start vnstat

vnstat will be running in the background, and you'll have to wait days for it to gather statistics and be able to show you, for instance, the average monthly usage.

› vnstat -m
# gif0: Not enough data available yet.

After a few minutes, you'll see stats on vnstat.

Last 5 minutes

› vnstat -5

# en0  /  5 minute
#
#         time        rx      |     tx      |    total    |   avg. rate
#     ------------------------+-------------+-------------+---------------
#     2023-03-19
#         12:45    839.44 MiB |    2.60 MiB |  842.04 MiB |   23.55 Mbit/s
#         12:50    226.26 MiB |  306.00 KiB |  226.56 MiB |   46.35 Mbit/s
#     ------------------------+-------------+-------------+---------------

Hourly

› vnstat -h

# en0  /  hourly
#
#         hour        rx      |     tx      |    total    |   avg. rate
#     ------------------------+-------------+-------------+---------------
#     2023-03-19
#         12:00      1.04 GiB |    2.90 MiB |    1.04 GiB |   28.10 Mbit/s
#     ------------------------+-------------+-------------+---------------

Monthly

› vnstat -m

# en0  /  monthly
#
#        month        rx      |     tx      |    total    |   avg. rate
#     ------------------------+-------------+-------------+---------------
#       2023-03      1.04 GiB |    2.90 MiB |    1.04 GiB |   28.10 Mbit/s
#     ------------------------+-------------+-------------+---------------
#     estimated      3.43 TiB |    9.56 GiB |    3.44 TiB |

You can read the guide to get familiar with the commands.

MARCH 20, 2023

Here are my highlights from Works Containing Material Generated by Artificial Intelligence.


One such recent development is the use of sophisticated artificial intelligence (“AI”) technologies capable of producing expressive material.[5] These technologies “train” on vast quantities of preexisting human-authored works and use inferences from that training to generate new content. Some systems operate in response to a user's textual instruction, called a “prompt.” [6] The resulting output may be textual, visual, or audio, and is determined by the AI based on its design and the material it has been trained on. These technologies, often described as “generative AI,” raise questions about whether the material they produce is protected by copyright, whether works consisting of both human-authored and AI-generated material may be registered, and what information should be provided to the Office by applicants seeking to register them.

[I]n 2018 the Office received an application for a visual work that the applicant described as “autonomously created by a computer algorithm running on a machine.” [7] The application was denied because, based on the applicant's representations in the application, the examiner found that the work contained no human authorship. After a series of administrative appeals, the Office's Review Board issued a final determination affirming that the work could not be registered because it was made “without any creative contribution from a human actor.”

In February 2023, the Office concluded that a graphic novel [9] comprised of human-authored text combined with images generated by the AI service Midjourney constituted a copyrightable work, but that the individual images themselves could not be protected by copyright.

In the Office's view, it is well-established that copyright can protect only material that is the product of human creativity. Most fundamentally, the term “author,” which is used in both the Constitution and the Copyright Act, excludes non-humans.

[I]n the current edition of the Compendium, the Office states that “to qualify as a work of 'authorship' a work must be created by a human being” and that it “will not register works produced by a machine or mere mechanical process that operates randomly or automatically without any creative input or intervention from a human author.”

Individuals who use AI technology in creating a work may claim copyright protection for their own contributions to that work.

Applicants should not list an AI technology or the company that provided it as an author or co-author simply because they used it when creating their work.

MARCH 17, 2023

docker run -it -p HOST_PORT:CONTAINER_PORT your-image

When you run services inside of Docker in specific ports, those are internal ports on the virtual container environment. If you want to connect to those services from your machine, you need to expose ports to the outside world explicitly. In short, you need to map TCP ports in the container to ports on the Docker host, which may be your computer. Here's how to do it.

Let's imagine we have a Next.js app running inside our Docker container.

› docker run -it my-app-image
next dev
# ready - started server on 0.0.0.0:3000, url: http://localhost:3000

The site is exposed to port 3000 of the container, but we can't access it from our machine at http://localhost:3000. Let's map the port.

› docker run -it -p 1234:3000 my-app-image
next dev
# ready - started server on 0.0.0.0:3000, url: http://localhost:3000
  • We've mapped TCP port 3000 of the container to port 1234 of the Docker host (our machine)
  • We can now browse the app at http://localhost:1234
  • When your machine loads port 1234, Docker forwards the communication to port 3000 of the container

MARCH 15, 2023

You can upload Shorts to YouTube with the YouTube API as you would upload any other video. Simply ensure your video has an aspect ratio of 9:16 and is less than 60 seconds. YouTube will automatically set it as a Short.

Follow this guide to see how to upload videos to YouTube with the YouTube API.

MARCH 10, 2023

Apple just unlocked new options to price apps: ten-cent steps between $0.10 to $10, fifty cents between $10 and $50, and so on and so forth.

Choose from 900 price points — nearly 10 times the number of price points previously available for paid apps and one-time in-app purchases. These options also offer more flexibility, increasing incrementally across price ranges (for example, every $0.10 up to $10, every $0.50 between $10 and $50, etc.).

FEBRUARY 24, 2023

Here's how to define simple async functions in TypeScript.

(async (/*arguments*/) => {/*function logic*/})(/*values*/); 

No arguments

// Define an asynchronous function.
const helloAsync = async() => { console.log("Hey, Async!"); }

// Call it asynchronously.
helloAsync();

With arguments

(async(text: string) => { console.log(text); })("Hello, Async!")

With delay

(async(text: string) => { setTimeout(() => console.log(text), 2000); })("Hello, Async!")

Synchronously inside of an asynchronous function

// Say we have an async talk() function that logs text to the console.
const talk = async(text: string) => { console.log(text); }

// And a sleep() function that uses a Promise to wait for milliseconds.
const sleep = (ms: number) => {
  return new Promise(resolve => setTimeout(resolve, ms));
}

// We can wrap calls to async functions in an async function.
// Then `await` to execute them synchronously.
(async () => {
  await talk(`Hello!`);
  await sleep(1000);
  await talk(`What's up?`);
  await sleep(2000);
  await talk(`Bye now!`);
})();

FEBRUARY 23, 2023

Here's how to list the commits that happened between two tags.

git log --pretty=oneline 0.8.0...0.9.0

The two tags—in this case, 0.8.0 and 0.9.0—need to exist.

You can list existing tags in a repository as below.

git tag

FEBRUARY 22, 2023

You can list what packages are installed globally in your system with npm -g list—shorthand for npm --global list—whereas you'd list the packages installed in an NPM project with npm list.

Let's see an example of what the command might return.

npm -g list
# /opt/homebrew/lib
# ├── cross-env@7.0.3
# ├── http-server@14.1.1
# ├── node-gyp@9.3.1
# ├── npm@9.5.0
# ├── pm2@5.2.2
# ├── spoof@2.0.4
# ├── ts-node@10.9.1
# └── typescript@4.9.5

Want to see older publications? Visit the archive.

Listen to Getting Simple .