AI 资讯
Paw & Order: upload your dog, and defend them against evidence generated from their own photo
This is a submission for the DEV Weekend Challenge: Dog Days Edition . Upload a photo of your dog. An AI accuses them of a crime. You're their defense attorney. What I Built Paw & Order is a browser game where your own dog is the defendant. You upload one photo and a few seconds later your dog has been arrested: The People vs. Biscuit Docket #PAW-042 DEFENDANT: Biscuit CHARGE: Grand Theft Sausage COUNSEL: You STATUS: Extremely suspicious Then the trial starts. The prosecutor puts a question to you, you pick a response, and the case branches from there. Three exhibits go into evidence: generated images of your dog, at the scene, with the frosting still on their muzzle. Two witnesses give statements, and at least one of them is usually lying. A trial runs a few minutes. At the end you get one of four verdicts: NOT GUILTY NOT GUILTY, BUT SUSPICIOUS GUILTY, BUT REASONABLE DOUBT GUILTY Plus a scoreline that isn't the same thing as winning: VERDICT NOT GUILTY Biscuit is free to commit additional crimes. Defense Performance: 94/100 You can lose the case and still score 96. You can win it badly. Every case has a hidden truth, generated before the trial begins. Sometimes the dog really did it, sometimes they're innocent, sometimes the evidence just lies. The client never sees any of it, so you're not hunting for a correct answer. You're building the strongest defense the facts allow. Choices decide the outcome. Replay the same case, answer differently, and the verdict and the score change with you. Demo Live: https://paw-order.pages.dev Bring a dog photo, or don't. The home page has a public docket of cases other players entered into the public record, and you can play any of them without uploading anything. Code ArjenPostma / Paw-Order dev.to weekend challenge submission Paw & Order Justice for every good boy. Upload a photo of your dog. AI generates a fictional criminal case around that dog. You defend them in court. Live: https://paw-order.pages.dev DEV Weekend Challenge:
AI 资讯
Solve It Once: Kelsey Hightower's Talk Applied to Security Verification
✓ Human-authored analysis; AI used for formatting and proofreading. Kelsey Hightower gave a talk at PlatformCon 2026 that was about the arc of a career, from running commands in SharePoint to writing Go tools that play music on your terminal. The stories has an architecture principle that applies to how security verification should work. Solve the problem once, encode the solution as a reusable artifact, and never solve that problem again. The Jira loop He joined a company where deployments were driven by Jira tickets. Someone opens a ticket with deployment parameters. An engineer would read the ticket, copy the parameters, run the commands, paste the output back into the ticket, close it, and wait for the next one. Every hour, another ticket. Same process, commands and manual steps. The engineer became the loop. He wrote a Puppet manifest that watches the tickets, extracts the parameters, runs the deployment, posts the output, and closes the ticket. The loop ran once as automation and then it was over. No engineer in the loop or ticket waiting for a human. The problem was solved, permanently, by encoding the solution into a reusable artifact. Doing a repetitive manual process faster is not the right thing to do. Eliminate the loop by recognizing the abstraction hiding in the repetition and encoding it into an artifact that makes the manual steps unnecessary. The substrate This is the pattern that runs through every transition he describes. It's missed by most people when they talk about automation. System administrators ran deploy.sh manually. Docker didn't automate typing apt-get install . Docker recognized that "application + dependencies + environment" was a repeatable unit. The container image became the substrate. Deployment stopped being a sequence of commands and became a declaration. The commands didn't get faster. They became unnecessary. Operators placed workloads on servers manually. Kubernetes didn't automate SSH-ing into machines to check available mem
AI 资讯
How We Got an LLM to Draw Charts Without Ever Touching a Pixel
Let's get something out of the way first. Having data is good. Having a database full of reviews, commits, and org activity sitting there quietly, untouched, unread, never once glanced at by a human being with a coffee and an opinion? That's not "having data." That's a very expensive data graveyard. At LiveReview , we build what we call a Blast-Radius Aware AI Code Review for Business-Critical Systems . Which is a fancy way of saying: we review your code, we figure out how bad it would be if a change goes wrong, and we don't shut up about it until someone fixes it. Along the way we accumulate a review data: who reviewed, how much, how fast, how often, which repos are on fire. And for a while, that pile just sat there. Engineering leaders would ask "is adoption increasing?" and get back a vibe, not an answer. So we built Livi , a chat bot that answers real questions about that data with real charts, not paragraphs of hedging. This post technically about how Livi draws those charts. Specifically: why we never let the LLM touch a pixel, how the same chart definition ends up as both a live interactive graph in your browser and a flat PNG in a Slack thread, and why teaching a language model to pick the right chart shape is a surprisingly deep rabbit hole. The core decision: don't ask the LLM to draw, ask it to describe The tempting, wrong idea is: "let's have the LLM generate an image." Please don't. Image-generating models are a different beast entirely, and even if you got one to draw a bar chart, you'd have no way to verify the numbers on it are real. You'd be trusting a model that hallucinates plausible-sounding review counts to also render them faithfully into pixels. That's not a chart, that's chart-shaped fan fiction. The actually good idea, and the one every serious LLM-charting integration eventually converges on, is: the LLM writes Vega-Lite , a JSON grammar for describing charts declaratively. You don't say "draw a blue bar going up." You say: { "mark" : "bar"
AI 资讯
Null Is Not Zero: Building a JavaScript SEO Audit That Admits Its Limits
We moved a server-side SEO engine into a Chrome extension. Measuring the page was the easy half. Saying what we could not measure was the hard half. We had been running an on-page analysis engine on our own servers for years. You give it a URL, it fetches the page, it reports. Ordinary. Then we moved that engine into the browser, because a server cannot reach localhost , a staging box, an intranet, or anything behind a login. The browser can. Porting the analysis was mechanical work. What took the real time was a category of problem that barely exists on the server: in a live tab, half the things you want to measure are sometimes unavailable, and the honest answer is not a number. This post is about the decisions that came out of that, with the code that implements them. The One Rule: Null Is Not Zero Every derivation in the engine returns number | null , and the two mean different things. 0 means we measured it and it is zero. A page with no layout shift really does score zero. null means we could not measure it. No interaction happened yet, the browser does not support that entry type, or the document came from another origin and the size fields were zeroed out. A zero printed where a null belongs is a made-up number. It is worse than an empty cell, because the reader has no way to tell it apart from a real measurement. So the two never collapse: the derivation keeps them separate and the UI renders them differently. That sounds obvious written down. It is surprisingly easy to violate, and the next section is the most common way. PerformanceObserver Fails Silently, So Ask It First Here is the trap. Calling observe() with an entry type the browser does not support does not throw . It does not warn. It quietly does nothing, and your handler is simply never called. Which means an unsupported metric produces exactly the same result as a measured zero. The one thing the rule above forbids. The fix is to ask before you observe, and to record the refusal: js const SUPPOR
AI 资讯
My First Time Putting an App on AWS (A Beginner's Story)
Today I did something I've wanted to do for a while — I took an app running on my own laptop and put it "live" on the internet using AWS. It sounds scary when you read about it online, but once I actually did it, it was just a bunch of small, simple steps, one after another. This post is me writing down everything I did, in plain, easy words, so that if you're a beginner like me, you can follow along without getting confused by fancy tech terms. What is AWS, in simple words? AWS (Amazon Web Services) is basically Amazon renting out computers over the internet. Instead of buying your own physical server and keeping it running 24/7 at home, you "rent" a computer from Amazon. That computer runs your app, and anyone with the internet can visit it. The specific service I used is called EC2 . Think of EC2 as one virtual computer that lives in Amazon's data center, and you get to control it like it's your own. Step 1: Set up IAM first Before touching any servers, I went to IAM (Identity and Access Management). This is AWS's way of managing "who is allowed to do what" in your account. In simple words: instead of using your main AWS login for everything (which is risky), IAM lets you create a separate user with its own permissions. It's like giving someone a spare key instead of your master key. I set this up first so my account stays safer. Step 2: Launch an EC2 instance Next, I went to the EC2 section and launched a new instance (a fancy word for "a virtual computer"). During this step, AWS also lets you create a .pem file — this is basically a secret key file. It's like a digital key to a lock. Only someone with this file can get into the server. I downloaded it and kept it safe, because if you lose it, you can't easily get back in. Step 3: Login to the server using SSH Once the server (EC2 instance) was ready, I needed a way to "log in" to it from my own laptop. For that, I used something called SSH, along with the .pem key file I downloaded earlier. In simple words: SSH
AI 资讯
I stopped letting LLMs guess financial facts
LLMs can be surprisingly useful for company research. But I kept running into a strange split: parts of the reasoning were useful, while the financial facts underneath them were much harder to trust. A model could identify an accounting risk in one paragraph, then mix fiscal periods, accounting scopes, or currencies in the next. Missing values might quietly become zeros. A deterministic calculation could be performed probabilistically. A citation could point to a real filing without actually supporting the claim. Those are different failure modes, and treating all of them as one giant prompting problem did not feel like a reliable architecture. So I started building OpenThesis , an Apache-2.0 desktop system for evidence-first, AI-assisted company research. The project is not a stock picker or a trading bot. The idea is simpler: use ordinary software for work that should be deterministic, and give the LLM a bounded evidence set for the reasoning work where it can actually help. The monolithic prompt is doing too many jobs A common company-research workflow looks roughly like this: company question ↓ LLM ↓ answer That single model call is implicitly responsible for remembering reported values, selecting the right fiscal period, recognizing the accounting scope, finding sources, performing calculations, comparing scenarios, identifying risks, and writing a conclusion. Some of those tasks are probabilistic by nature. Others are not. Qualitative reasoning, connecting evidence, forming scenarios, and challenging an assumption are reasonable uses of a language model. Remembering an exact reported value, deciding whether a value is missing, and calculating a margin or valuation are poor places to accept probabilistic behavior. My design rule became: Deterministic work should stay deterministic. Use LLMs for reasoning, not as the database and calculator underneath the reasoning. Evidence before reasoning OpenThesis starts from official filings rather than from model memory o
AI 资讯
Shipping a vision-model verdict on Bedrock and Lightsail
Built 2026-08-15 against us.amazon.nova-lite-v1:0 via the Bedrock Converse API. FastAPI on Python 3.13, deployed to an Amazon Lightsail container service ( nano , scale 1) in us-east-1 . Scored against the live deployment, not localhost: 20/20 on the fixture set, median 880 ms per scan. Live: Dog or Not: Lite · Source: github.com/xbill9/dog-or-not-lite · Built for the AWS Weekend Challenge: Build a Creative App . TL;DR Make the model fill in a schema instead of writing a sentence. The Converse API's toolConfig plus toolChoice forces a named function call, so is_dog arrives as a boolean because it was declared as one. Every image comes back in the same shape — including the ambiguous ones, which is exactly where free-text output gets creative and a string-matching parser gets it wrong. The app is a webcam scanner that tells you whether the thing you are holding up is a dog. One HTML page, one POST /api/scan , one model call, no build step, no framework. The whole backend is 285 lines. Three AWS specifics are worth the price of admission: Lightsail container services have no IAM task role. There is nothing to attach a policy to, so the container needs a real access key as an environment variable. The mitigation is scope, not secrecy. A cross-region inference profile is authorized against every region it routes to. With the policy pinned to us-east-1 , a call made to us-east-1 was denied naming us-west-2 . Measured, not inferred. --platform linux/amd64 is not optional. An arm64 image builds, pushes and deploys cleanly, then crash-loops with an exec format error that never mentions architecture. And a mock mode that answers every scan locally is what made the frontend free to build — no credentials, no model access, no bill. 1. The shape: one route, one call The classification rule is the only opinionated part. is_dog is true only for a living domestic dog: a wolf is not a dog , nor is a coyote, fox, plush toy, bronze statue, cartoon, or person in a costume. That is a c
科技前沿
Samsung Galaxy Z Fold8 and Galaxy Z Fold8 Ultra Review: The Right Shape
Nearly a decade after its debut, Samsung’s Galaxy Fold finally comes into its own.
开发者
GOOD DOG: you are the dog, and the dog is real
This is a submission for Weekend Challenge: Dog Days Edition What I Built A game where...
AI 资讯
TerraMow V1000 Review: Show Your Lawn Some Love
With automatic mapping, Spot Mode functionality, and smart AI cameras, the TerraMow V1000 is the complete package.
AI 资讯
WikiPaw - Dog hunt through Wiki hopping
This is a submission for Weekend Challenge: Dog Days Edition What I Built WikiPaw is an interactive Wikipedia-hopping game designed around dog breeds! Players are given a target dog breed to reach but start on a Wikipedia page located 2–3 outgoing link hops away from their target. To help navigate the maze of Wiki links, WikiPaw uses Gemini AI as an intelligent guide to evaluate your current page against the target breed and hint at how close you are to reaching your destination Demo Live project coming on Wikipaw wikipaw-demo.mov - Google Drive drive.google.com Code The code is hosted on my github and repo is called wikipaw How I Built It The following points describe how the project works: Core Game Loop: We construct a graph of Wikipedia links starting from a selected dog breed, traversing backwards 2–3 hops to select a fun starting article. Gemini AI Integration: On each page visit, the current Wikipedia article content and target breed details are sent to Gemini AI. The model analyzes semantic similarity, topical relevance, and contextual overlap to calculate a "proximity score" and generate dynamic hints for the player. Frontend/Backend: Built with a clean UI to render stripped Wikipedia content with active internal links while tracking the player's path and hop count. Prize Categories Best Use of Google AI : Leveraged Gemini AI to dynamically calculate semantic proximity between Wikipedia articles and generate context-aware hints for players.
AI 资讯
I Logged Every AI Crawler for 34 Days. ChatGPT Outreads Googlebot
In mid-July, my Google clicks in my home market (Israel) dropped by almost half. Buyer-intent queries that used to bring steady leads just evaporated from Search Console. While I was staring at GSC dashboards trying to figure out what broke, I finally did the thing I should have done months earlier: I stopped looking at dashboards and started reading raw server logs. What I found there was a parallel universe. Google Search was sending me less than ever — but AI systems were reading my site constantly . Not "someday this will matter" constantly. Right-now constantly: an AI assistant was fetching a page of mine roughly every 26 minutes, around the clock, because a real human had just asked it a question. So I built a small log analyzer and let it run. Here's what 34 days of complete Caddy logs from a small business site (about 70 real human visitors a day) actually look like. The numbers All counts are HTTP 200 responses only (more on why below), over 34 days: Bot Requests Per day What it is bingbot 5,444 158.2 Bing's index — which feeds ChatGPT ChatGPT-User 1,388 40.3 Live fetch while a human asks ChatGPT Googlebot 1,233 35.8 Classic Google crawl GPTBot 547 15.9 OpenAI training crawler Claude-User 519 15.1 Live fetch while a human asks Claude OAI-SearchBot 281 8.2 ChatGPT search indexing Applebot 268 7.8 Apple (Siri / Apple Intelligence) ClaudeBot 214 6.2 Anthropic training crawler Amazonbot 136 4.0 Amazon (Alexa & co.) PerplexityBot 103 3.0 Perplexity indexing Three things in that table genuinely surprised me. ChatGPT-User outreads Googlebot. 40.3 fetches a day versus 35.8. This isn't a crawler building an index for later — ChatGPT-User is the user-agent OpenAI sends when a human is mid-conversation and ChatGPT decides to pull a live page to answer them. On my site, that now happens more often than Googlebot visits. For a tiny business site in a niche market, I did not expect that. Bing crawls 4.4x harder than Google. 158 requests a day versus 36. Nobody optimizes
AI 资讯
Docker Compose - orquestrando múltiplos containers
1. Retomando: do docker run repetido a um arquivo único No artigo anterior, subir uma API e um Postgres conectados exigiu dois comandos docker run longos, com flags de rede, volume e variáveis de ambiente para lembrar (e digitar) toda vez. Em um projeto real, com mais serviços — cache, fila, worker em background — isso rapidamente vira inviável de manter na cabeça ou em um script solto. O Docker Compose resolve isso descrevendo toda a aplicação multi-container em um único arquivo declarativo, versionado junto com o código. 2. O arquivo compose.yaml Compose lê um arquivo YAML (por convenção compose.yaml , ou o nome legado docker-compose.yml , ainda amplamente usado) descrevendo serviços (cada um vira um ou mais containers), redes e volumes: # compose.yaml services : api : build : . ports : - " 8000:8000" environment : DATABASE_URL : postgresql://postgres:segredo@banco:5432/postgres depends_on : - banco banco : image : postgres:16 environment : POSTGRES_PASSWORD : segredo volumes : - pg-dados:/var/lib/postgresql/data volumes : pg-dados : Isso substitui inteiramente os dois docker run do artigo anterior. Uma diferença importante já aparece aqui: por padrão, Compose cria uma rede própria para o projeto e conecta todos os serviços a ela automaticamente — não é preciso um docker network create manual, nem declarar --network em cada serviço. Cada serviço já é acessível pelos demais pelo nome declarado em services: (aqui, banco resolve para o container do Postgres), exatamente como as redes definidas pelo usuário do artigo anterior. 3. Comandos essenciais do Compose docker compose up -d # sobe todos os serviços em segundo plano docker compose ps # lista os containers do projeto e seu status docker compose logs -f api # segue os logs de um serviço específico docker compose logs -f # segue os logs de todos os serviços, intercalados docker compose exec api bash # abre um shell dentro do container de um serviço docker compose stop # para os containers sem removê-los docker comp
AI 资讯
Trend: Amodei predicts 1-person billion-dollar company
Dario Amodei Is Right. But He Is Missing the Hard Part. Dario Amodei said the first billion-dollar company with one employee would appear in 2026. He put 70-80% probability on it. I am not building a billion-dollar company. But I am running something that does the work of several teams: 86 containers, 24 databases, 240 cron jobs, two servers, one person. Amodei is right that this is now possible. The tools exist. The costs dropped. A full AI stack costs me between $3,000 and $12,000 per year. The equivalent in human headcount would run $80,000 to $120,000 per month. But the headline version of the "one-person company" story skips the hard part. It sounds like you hire an AI, fire your team, and go make money. That is not what happened for me. What actually happened was eighteen months of building a system that makes "one person" sustainable at 3 AM when something breaks and nobody is awake to fix it. Here is what that system looks like in practice. The Stack Is Not the System Most people stop at the stack. They pick Claude or GPT, wire up a few automations, and call it an AI-powered business. That works until the first thing breaks in a way the model did not anticipate. The stack I run includes SaaS apps for golf clubs, a school management platform, an auth provider, a CRM, a community platform, and several tools for my own operations. Each of these runs in Docker containers managed by Coolify, spread across two Hetzner servers in Germany. That part is table stakes. Any competent developer can set up containers. The system is what sits on top. It is what makes the difference between "one person with a lot of tools" and "one person running a business that actually works." Guard Rules: The Thing That Catches What You Miss I wrote about this in detail in Runs Without Me : the biggest risk in a one-person setup is not that the AI does something wrong. It is that you do not notice until hours or days later. My setup uses 177 guard files that intercept operations before t
开发者
Cómo solucionar `docker run` con `Exited (1)` en Raspberry Pi
Cómo solucionar docker run con Exited (1) en Raspberry Pi ¿Por qué ocurre este error? El código de salida 1 indica que el proceso principal del contenedor terminó con un error genérico. En Raspberry Pi, los casos más comunes son: Arquitectura incompatible : La imagen fue construida para amd64 (x86_64), pero Raspberry Pi usa arm32v7 o arm64v8 . Falta de binarios compatibles : El ENTRYPOINT o CMD del contenedor intenta ejecutar un binario compilado para otra arquitectura. Problemas de permisos o dependencias faltantes en el entorno embebido (especialmente en Raspberry Pi OS Lite sin GUI). Uso incorrecto de --net=host : En algunas versiones de Docker en Raspberry Pi, el flag --net=host puede causar fallos si el sistema no lo soporta correctamente. 🔍 Nota crítica : En tu comando original docker run --net = host -d -t myimage , hay un error de sintaxis: --net = host tiene espacios alrededor del = . Docker lo interpreta como un nombre de red literal " = host" , lo que probablemente falla. Pasos para solucionarlo Paso 1: Corrige la sintaxis del comando # ❌ Incorrecto (con espacios en `--net`) docker run --net = host -d -t myimage # ✅ Correcto (sin espacios) docker run --net host -d -t myimage ⚠️ Importante : En Docker CLI, los flags con valores no deben tener espacios entre el = . Usa --net=host o --net host , pero nunca --net = host . Paso 2: Verifica la arquitectura de la imagen Ejecuta en tu Raspberry Pi: docker inspect myimage --format '{{.Architecture}}' Si el resultado es amd64 , la imagen no es compatible con Raspberry Pi . Solución: Reconstruir la imagen para ARM Si tienes el Dockerfile , usa multi-arch build: # Al inicio del Dockerfile (antes de FROM) # syntax=docker/dockerfile:1 FROM --platform=$BUILDPLATFORM golang:1.21-alpine AS builder ... O construye explícitamente para ARM: # En tu máquina de desarrollo (x86_64) docker buildx create --use docker buildx build --platform linux/arm/v7 -t myimage:armv7 . --push # o para Pi 4 (64-bit): docker buildx build --platf
AI 资讯
Trend: Forbes Solo-Founder AI Playbook
Forbes Called It a Playbook. I Call It a Production Log. Forbes published a piece recently calling AI agent startups "the new solo-founder playbook." I read it twice. The framing bothered me both times. A playbook implies steps. A sequence. Something you can hand to someone and say: follow this, and you will get the result. What Forbes described is not that. It is a description of an outcome, written by people who did not have to fix anything at 2 AM when the agent broke. Let me tell you what it actually looks like. The Night 871 Emails Went to the Wrong People Fourteen months ago I built my first agent that could send emails on behalf of the system. It was an outreach automation, nothing exotic. The agent would identify leads, draft a message, and send it after a human approval step. Except the approval step had a race condition. Two concurrent jobs both read "pending" from the database, both approved, and both dispatched. One lead received 871 emails over 40 minutes before I caught it. No company, no legal team, no PR buffer. Just me and an inbox full of angry replies. That night I wrote my first hard guardrail: #!/bin/bash # email-dedup-guard.sh LEAD_ID = " $1 " LOCK_FILE = "/tmp/email-lock- ${ LEAD_ID } " if [ -f " $LOCK_FILE " ] ; then echo "BLOCK: email already dispatched for lead ${ LEAD_ID } " > &2 exit 1 fi touch " $LOCK_FILE " # proceed with send Embarrassingly simple. But I did not know I needed it until I needed it. This is what Forbes leaves out. The playbook is written in retrospect, after someone else absorbed the cost of learning. The Model Is Not the Problem Every conversation about AI agents eventually becomes a conversation about which model to use. GPT-4 versus Claude versus Gemini. Benchmarks and context windows and reasoning scores. Here is what I learned: the model is the easy part. My current system runs 86 containers across two Hetzner servers. 240 automated jobs. Every day, these jobs do things: post content, process leads, trigger builds,
AI 资讯
I Didn't Mean to Build a Programming Language
I'm building a programming language. Written like that, it sounds as if I had always dreamed about compilers, read the Dragon Book cover to cover, and spent years waiting for the day I could finally design my own language. Not even close. I was just writing ordinary web applications and constantly thinking things like: "Why do I have to write it this way here?" or: "Wouldn't this feel better if I could write it a little more directly?" I kept digging into those small annoyances instead of ignoring them, one by one, and somehow they turned into a programming language. It's called Seseragi . Seseragi (せせらぎ) is a Japanese word for the gentle sound or flow of a small stream. I wanted my programming language to have a Japanese name. https://github.com/KentaroMorishita/seseragi https://seseragi.vercel.app/ https://seseragi.vercel.app/tour/ It's still experimental and pre-release, but a Rust compiler, CLI, LSP, formatter, WASM Playground, Signal, and Web UI are already working to a surprising degree. Even I sometimes look at it and think, "How far is this thing going?" It started with being tired of if In 2024, I wrote this article on Qiita. https://qiita.com/KentaroMorishita/items/6329d20fbc6f98f72864 The title alone probably tells you I was already heading somewhere weird. I don't think I hated if itself. What bothered me was the feeling of tracing conditional branches as statements . That was also why I liked ternary expressions. Not just because they were short. They were expressions, so I could take the result directly as a value. const label = isLoading ? " Loading... " : hasError ? " Error " : " Ready " Of course, once these grow, they become painful too. So I started building my own match and when abstractions on top of TypeScript. Looking back, I was trying pretty hard to fight the language. But the underlying desire was already clear: I'd rather construct values than chase control flow. When I look at Seseragi now, the symptoms had started long before the languag
AI 资讯
Soft Boil — six minutes, and you cannot get it wrong
This is a submission for Frontend Challenge - Comfort Food Edition, CSS Art . Inspiration My other two entries were about a moment and a ritual. This one is about the opposite: the dish you fall back on when you have no skill, no energy and no plan. Boiled eggs are what you make when you cannot cook. Six minutes, one pan,and the comfort is precisely that it is not possible to get it wrong . Two choices made it worth drawing rather than just worth eating. A glass bowl, so you can see the boil. In a steel pan the interesting half of this is hidden. Glass also turned out to be the exact opposite problem to the terracotta in my chai piece — unglazed clay is matte and forgives a sloppy gradient, glass shows you every single one. An induction hob, for the light. I finished the fridge piece saying the one piece of advice I'd give is pick a scene with a light source in it . So I did it again on purpose. The element ring is the only warm thing in an otherwise cold grey kitchen, and it lights the water from underneath. Everything here is a div , a gradient or a shadow. No SVG, no images, no canvas. Demo Press Turn off the heat and give it a few seconds. The ring dies back, the bubbles thin out, and the eggs slowly stop moving — then put it back on and watch the pan come to the boil in stages. That build-up is the part I'd most like you to see, and it's the whole subject of this post. Journey Nothing in this picture is transparent The obvious way to draw a glass bowl is backdrop-filter . I'd advise against building a picture on it — support is uneven enough that the piece falls apart somewhere, and it's expensive. So the transparency is painted. The water is drawn first as its own element, and then a front wall of highlights sits over the top of it: two vertical speculars down the sides for the curve of the glass, a soft wash across the middle for the thickness of the pane, and a rolled lip at the top, which is the one place glass is genuinely opaque enough to draw as a solid.
AI 资讯
What the browser can actually tell you about your hardware (and what it can't)
I spent a while building browser-based hardware diagnostics and came away with a much clearer sense of where the web platform is genuinely capable and where it quietly lies to you. Notes below, with live demos for each API so you can poke at them yourself. Refresh rate: requestAnimationFrame is the only signal you get There's no screen.refreshRate . The only approach is timing requestAnimationFrame callbacks and inferring the rate from the median frame delta: const deltas = []; let last = performance . now (); function tick ( now ) { deltas . push ( now - last ); last = now ; if ( deltas . length < 180 ) requestAnimationFrame ( tick ); else { const sorted = deltas . slice (). sort (( a , b ) => a - b ); console . log ( Math . round ( 1000 / sorted [ sorted . length >> 1 ])); } } requestAnimationFrame ( tick ); Two gotchas that cost me time. Use the median , not the mean — a single dropped frame wrecks an average. And browsers throttle rAF in background tabs, so the measurement is meaningless unless the tab is visible; gate it on document.visibilityState . ( live version ) Screen dimensions: four different answers, all "correct" screen.width , window.innerWidth , window.devicePixelRatio and screen.availWidth measure genuinely different things, and the one people usually want — actual native panel resolution — is screen.width * devicePixelRatio . Except that's still CSS-pixel derived, so on a scaled display it can disagree with what the panel physically is. The browser simply does not expose true hardware resolution. ( demo ) Keyboard: event.code vs event.key , and the keys you never receive event.key is layout-dependent, event.code is physical position — for a hardware tester you want code . The real limitation is that some keys never reach JS at all: PrintScreen often doesn't fire keydown , Meta combinations get swallowed by the OS, and Fn isn't a browser-visible key on most laptops. N-key rollover testing works surprisingly well though, since you just track the siz
AI 资讯
Four patterns that keep my YouTube longform JSON queue from going stale
I manage the YouTube longform queue for my BuilderStack channel as JSON files in content/yt-longform-queue/ . A spec file lands there when a generator script commits a new dialogue; the publish workflow picks the file, renders it to MP4, uploads it, then moves the file to uploaded/ . No external queue service, no database rows, no management dashboard. This has worked for three months without a major incident. Four patterns kept it from collapsing. Archetype-priority picking, not FIFO First-in, first-out publishing breaks when you have product walkthrough videos, educational deep-dives, and weekly recap specs all in the queue simultaneously. A recap spec committed yesterday would block a product walkthrough from two weeks ago if the queue ran FIFO — and the product content is what actually grows the channel. The picker uses an explicit priority rank: RANK = { " product_findindiegame " : 0 , " product_ossfind " : 1 , " hidden-gem " : 1 , " build_in_public " : 2 , " technical " : 3 , " curated " : 4 , " meta " : 4 , " contrarian " : 6 , " recap " : 7 , " ai_tools " : 7 , } DEFAULT_RANK = 5 Archetypes not in the dict fall to DEFAULT_RANK = 5 — the middle, not the bottom. New formats I haven't classified yet still air rather than sitting perpetually at the end. Within each rank tier, files sort by filename (oldest-first). The archetype value comes from the spec JSON's top-level archetype field, falling back to a prefix match on the filename for older files that predate the field. One consequence: adding a new archetype name to the dict can reorder the queue overnight. I've done this intentionally to let a backlogged product video jump ahead of a stale recap. 21-day stale expiry Queue files include a date prefix: YYYY-MM-DD-<slug>.json . The picker removes files whose date is more than 21 days old before selecting what to publish: MAX_AGE_DAYS = " ${ QUEUE_MAX_AGE_DAYS :- 21 } " CUTOFF = $( date -u -d " ${ MAX_AGE_DAYS } days ago" +%Y-%m-%d ) for f in content/yt-longform