Kasparov was the best chess player alive.
A machine ended that in 1997.
He didn't get worse. The world changed the question.
This is happening to careers right now — and most of the time, it's not your fault.
Kasparov was the best chess player alive.
A machine ended that in 1997.
He didn't get worse. The world changed the question.
This is happening to careers right now — and most of the time, it's not your fault.
Still opening an interactive rebase just to fix a commit message? Git 2.54 ships `git history reword` and `git history split` to end that pain. Hooks in config, smarter repack by default.
Update now 👇
https://chat-to.dev/view_trend?id=aytGelYyOXZEc0lzbG9TQVNTMExyZz09
Most devs reach for a library. The Canvas API already does this natively 🎨
I rebuilt a classic HTML5 text renderer in React — gradients, shadows, live controls, zero dependencies.
Read it → https://chat-to.dev/post?id=bWFXOVA0SnhNMVBVVFNGbmZwamdBZz09
#React #WebDev #JavaScript #Frontend
The HTML5 Canvas API has been around for over a decade, yet it still catches people off guard the first time they use it. The idea is straightforward: the browser hands you a low-level rendering context, and you paint whatever you want, pixel by pixel. In this tutorial we will build an interactive **Text Arranger**, exactly the kind of project from the original 2014 post, but this time using React and the patterns we actually reach for today. By the end you will understand how the Canvas handles text, gradients, and shadows, and you will have a reusable component you can drop into any project. --- ## What the Canvas is and why it still matters The `<canvas>` element is essentially a bitmap that the browser exposes to JavaScript. Unlike SVG, which describes shapes in XML and keeps a manipulable node tree, the Canvas holds no state: when you clear and redraw, the previous image is simply gone. That makes it ideal for animations, games, and anything that demands high rendering throughput. For text specifically, the Canvas gives you fine-grained control over font family, weight, style, alignment, and baseline, along with native support for gradients and patterns via `fillStyle`. You cannot get that level of per-pixel control by applying CSS to a `<div>`. --- ## How the React approach differs The original post attached `addEventListener` directly to DOM elements and called a `drawScreen()` function on every change. In React, the mental model is different: state lives inside the component, and the canvas is redrawn as a side effect whenever that state changes. This maps naturally to `useState` for the controls and `useEffect` for the drawing cycle. ```jsx import { useRef, useState, useEffect } from "react" const CANVAS_WIDTH = 600 const CANVAS_HEIGHT = 300 export default function TextArranger() { const canvasRef = useRef(null) const [text, setText] = useState("Hello, Canvas!") const [fontSize, setFontSize] = useState(50) const [fontFamily, setFontFamily] = useState("serif") const [fontWeight, setFontWeight] = useState("normal") const [fontStyle, setFontStyle] = useState("normal") const [fillType, setFillType] = useState("color") // color | linearGradient | radialGradient const [color1, setColor1] = useState("#e63946") const [color2, setColor2] = useState("#457b9d") const [fillOrStroke, setFillOrStroke] = useState("fill") // fill | stroke | both const [alpha, setAlpha] = useState(1) const [shadowX, setShadowX] = useState(2) const [shadowY, setShadowY] = useState(2) const [shadowBlur, setShadowBlur] = useState(4) const [shadowColor, setShadowColor] = useState("#00000066") const [textAlign, setTextAlign] = useState("center") const [textBaseline, setTextBaseline] = useState("middle") useEffect(() => { const canvas = canvasRef.current const ctx = canvas.getContext("2d") // Clear the previous frame ctx.clearRect(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT) // Background ctx.globalAlpha = 1 ctx.shadowColor = "transparent" ctx.fillStyle = "#f1faee" ctx.fillRect(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT) ctx.strokeStyle = "#a8dadc" ctx.lineWidth = 3 ctx.strokeRect(6, 6, CANVAS_WIDTH - 12, CANVAS_HEIGHT - 12) // Text settings ctx.font = `${fontWeight} ${fontStyle} ${fontSize}px ${fontFamily}` ctx.textAlign = textAlign ctx.textBaseline = textBaseline ctx.globalAlpha = alpha // Shadow ctx.shadowColor = shadowColor ctx.shadowOffsetX = shadowX ctx.shadowOffsetY = shadowY ctx.shadowBlur = shadowBlur const x = CANVAS_WIDTH / 2 const y = CANVAS_HEIGHT / 2 const metrics = ctx.measureText(text) const w = metrics.width // Choose the fill style let style if (fillType === "color") { style = color1 } else if (fillType === "linearGradient") { const grad = ctx.createLinearGradient(x - w / 2, y, x + w / 2, y) grad.addColorStop(0, color1) grad.addColorStop(1, color2) style = grad } else if (fillType === "radialGradient") { const grad = ctx.createRadialGradient(x, y, 1, x, y, w / 2) grad.addColorStop(0, color1) grad.addColorStop(1, color2) style = grad } // Draw if (fillOrStroke === "fill" || fillOrStroke === "both") { ctx.fillStyle = style ctx.fillText(text, x, y) } if (fillOrStroke === "stroke" || fillOrStroke === "both") { ctx.strokeStyle = style ctx.lineWidth = 2 ctx.strokeText(text, x, y) } }, [ text, fontSize, fontFamily, fontWeight, fontStyle, fillType, color1, color2, fillOrStroke, alpha, shadowX, shadowY, shadowBlur, shadowColor, textAlign, textBaseline, ]) return ( <div> <canvas ref={canvasRef} width={CANVAS_WIDTH} height={CANVAS_HEIGHT} style={{ display: "block", marginBottom: "1rem" }} /> {/* Controls */} <label> Text <input value={text} onChange={e => setText(e.target.value)} /> </label> <label> Size: {fontSize}px <input type="range" min={10} max={200} value={fontSize} onChange={e => setFontSize(Number(e.target.value))} /> </label> <label> Font family <select value={fontFamily} onChange={e => setFontFamily(e.target.value)}> <option value="serif">Serif</option> <option value="sans-serif">Sans-serif</option> <option value="monospace">Monospace</option> <option value="cursive">Cursive</option> <option value="fantasy">Fantasy</option> </select> </label> <label> Weight <select value={fontWeight} onChange={e => setFontWeight(e.target.value)}> <option value="normal">Normal</option> <option value="bold">Bold</option> <option value="lighter">Lighter</option> </select> </label> <label> Style <select value={fontStyle} onChange={e => setFontStyle(e.target.value)}> <option value="normal">Normal</option> <option value="italic">Italic</option> <option value="oblique">Oblique</option> </select> </label> <label> Fill type <select value={fillType} onChange={e => setFillType(e.target.value)}> <option value="color">Solid color</option> <option value="linearGradient">Linear gradient</option> <option value="radialGradient">Radial gradient</option> </select> </label> <label> Color 1 <input type="color" value={color1} onChange={e => setColor1(e.target.value)} /> </label> <label> Color 2 <input type="color" value={color2} onChange={e => setColor2(e.target.value)} /> </label> <label> Mode <select value={fillOrStroke} onChange={e => setFillOrStroke(e.target.value)}> <option value="fill">Fill</option> <option value="stroke">Stroke</option> <option value="both">Both</option> </select> </label> <label> Alpha: {alpha} <input type="range" min={0} max={1} step={0.01} value={alpha} onChange={e => setAlpha(Number(e.target.value))} /> </label> <label> Shadow X: {shadowX} <input type="range" min={-50} max={50} value={shadowX} onChange={e => setShadowX(Number(e.target.value))} /> </label> <label> Shadow Y: {shadowY} <input type="range" min={-50} max={50} value={shadowY} onChange={e => setShadowY(Number(e.target.value))} /> </label> <label> Shadow blur: {shadowBlur} <input type="range" min={0} max={50} value={shadowBlur} onChange={e => setShadowBlur(Number(e.target.value))} /> </label> <label> Shadow color <input type="color" value={shadowColor.slice(0, 7)} onChange={e => setShadowColor(e.target.value)} /> </label> <label> Horizontal alignment <select value={textAlign} onChange={e => setTextAlign(e.target.value)}> <option value="center">Center</option> <option value="left">Left</option> <option value="right">Right</option> </select> </label> <label> Baseline <select value={textBaseline} onChange={e => setTextBaseline(e.target.value)}> <option value="middle">Middle</option> <option value="top">Top</option> <option value="bottom">Bottom</option> <option value="alphabetic">Alphabetic</option> <option value="hanging">Hanging</option> </select> </label> </div> ) } ``` --- ## Understanding each piece **`useRef` for the canvas** React should not manage the canvas as a controlled element, because what lives inside it is not JSX: it is the result of imperative calls to the drawing API. `useRef` gives us direct access to the DOM node without triggering a re-render. **`useEffect` as the rendering engine** The dependency array at the end of `useEffect` is what connects React's declarative world to the Canvas's imperative one. Every time any piece of state changes, the effect runs from scratch: it clears the canvas and redraws everything. This pattern is intentionally simple. In applications with continuous animations you would use `requestAnimationFrame` inside the effect, but for a static tool like this one, re-rendering on each change event is both sufficient and easy to follow. **Gradients** Creating a gradient on the Canvas always requires absolute coordinates. For a horizontal linear gradient centered on the text, we calculate the text width with `ctx.measureText(text).width` and position the gradient from `x - w/2` to `x + w/2`. For the radial version, we use the same center with different radii to produce a halo effect. Due to character limits, here is the final part.
Tim Cook stepping down as Apple CEO in September. John Ternus, the engineer behind iPad, AirPods, and iPhone Air, takes over. One of the most carefully planned leadership transitions in tech history. 🍎
https://chat-to.dev/view_trend?id=dFVpc3NZS1VzODcyNEcySXJaVDBwdz09
What if AGI isn’t coming… but already here?
Most people are still debating the future while ignoring what’s happening now.
This will make you rethink everything 👇
https://comuniq.xyz/post?t=880
If you tried using ChatGPT today and thought your internet was acting up, relax, it wasn’t just you. This Monday, the platform went down for a lot of people around the world. The first reports started around 10 AM New York time, when users began noticing the chat wouldn’t load or would suddenly stop responding. Within a short time, thousands of users were already complaining. Some couldn’t even log into their accounts, while others could get in but couldn’t access past conversations or generate responses. OpenAI confirmed there was an issue and said it was investigating a problem affecting both ChatGPT and Codex. The company didn’t explain exactly what caused the outage, but made it clear it was an internal issue, not something on the user’s side. The good news is that the service started coming back for some users a few hours later, although there were still some instability issues in certain cases. Moments like this show just how much tools like ChatGPT have become part of everyday life. When it goes down, it feels like half the internet goes down with it. https://www.techradar.com/news/live/chatgpt-down-april-2026