Activity Feed
#6105·Dennis Hackethal, about 17 hours agoFiction is the best material
Why fiction and not factually accurate epistemology?
Let's jive on this one. What factually accurate epistemology texts/videos gives us good descriptions of "the people condition" - sounds awkard - in the meaning of "the human condition" and/or how humans work?
#6104·Dennis Hackethal, about 17 hours agoHi Anders, welcome to V 👋
To get started, take a look at the existing discussions or read How Veritula Works.
Which discussion interests you the most?
Thanks buddy... will do!
Fiction is the best material
Why fiction and not factually accurate epistemology?
Hi Anders, welcome to V 👋
To get started, take a look at the existing discussions or read How Veritula Works.
Which discussion interests you the most?
OEIS A307491 and A307492 Are Complete: A Proof with Oranges and Elliptic Curves
Photo by Jen Gunter on Unsplash
Some numbers of oranges can be laid flat as a triangle with one orange exactly in the middle. Some can be stacked into a neat pyramid. Very few numbers can do both.
So far, people have known five numbers that can: 1, 4, 10, 4960 and 428536. Computers had searched up to numbers with 29 digits without finding a sixth, but nobody knew whether one was hiding further out.
I asked an AI (Anthropic’s Claude) to discover something new, and it produced a proof that there is no sixth number. The five known ones are all there are.
In mathematical terms: the sequence A307491 in the OEIS (the On-Line Encyclopedia of Integer Sequences), “Numbers that are both centered triangular and tetrahedral”, is finite and complete. (‘Tetrahedral’ basically means: having the shape of a pyramid with a triangular base.) The proof finds all lattice points on an elliptic curve, meaning all points whose coordinates are whole numbers. (Mathematicians call these ‘integral points’. The term comes from ‘integer’ and has nothing to do with integrals in calculus.) The same method shows that the sequence for A307492, “Numbers that are both centered square and square pyramidal”, is complete as well: 1, 5, 42925, 1026745.
This article explains the puzzle and then walks through both proofs. They rely on computer algebra software and have not yet been checked by a mathematician, so criticism is welcome. Simply highlight any passage you think is wrong and click ‘Criticize’ in the menu that appears.
The puzzle: which numbers are both centered triangular and tetrahedral?
Take 4 oranges. You can lay them flat – one in the middle, three around it – to make a triangle. Or you can build a little pyramid: three on the bottom, one on top.

Both shapes are drawn from above. The flat shape on the left is a single layer. The pyramid has two layers, with the dark orange sitting on top of the other three.
10 oranges, the next number in the sequence, work too. The flat shape is the bowling-pin triangle, which has one orange in its exact middle. The pyramid has layers of 6, 3, and 1.

In simple terms, the question is: which numbers of oranges can you turn into a flat triangle and then rearrange into a pyramid?

Most numbers fail. Flat centered triangles need 1, 4, 10, 19, 31, 46, … oranges. Pyramids need 1, 4, 10, 20, 35, 56, … oranges. A number only counts if it is on both lists. After 10 the lists drift apart, and the next matches are 4960 (a pyramid 30 layers tall) and 428536 (136 layers tall).

Mathematicians call the first list the centered triangular numbers (A005448) and the second the tetrahedral numbers (A000292). The numbers on both lists form A307491. Its OEIS entry says if a sixth term a(6) exists, it’s larger than 1029.
Why a computer search isn’t enough
A search can only ever say ‘nothing up to here’. Numbers never end, so no search rules out a sixth match. To close the question you need an argument that covers all numbers at once. That argument is the proof.
From figurate numbers to a Diophantine equation
A ‘figurate’ number is a number of dots (or oranges) that can be arranged into a regular geometric shape, like a triangle, square, or pyramid. Tetrahedral and centered triangular numbers are both examples. A Diophantine equation is one where only whole-number solutions count, named after the ancient Greek mathematician Diophantus. We want such an equation because we can’t have a fraction of a layer or ring.
First we need a formula for each shape. A centered triangle has one orange in the middle, and its rings hold 3, 6, 9, … oranges. With k rings, that adds up to
(3k² + 3k + 2)/2
oranges. (For k = 1 this gives 4, and for k = 2 it gives 10.) A pyramid’s layers hold 1, 3, 6, 10, … oranges, counting from the top. With m layers, that adds up to
m(m + 1)(m + 2)/6
oranges. (For m = 2 this gives 4, and for m = 3 it gives 10.) A number can make both shapes exactly when the two formulas give the same value. So the puzzle is a Diophantine equation: find all whole numbers m and k such that
(3k² + 3k + 2)/2 = m(m + 1)(m + 2)/6
The fractions and the mix of k² and k make this equation awkward to work with, so we tidy it up in two moves. First, multiply both sides by 24. (Multiplying by 12 would already clear the fractions, but the extra factor of 2 keeps everything in whole numbers in the next move.) The left side becomes 36k² + 36k + 24 and the right side becomes 4m³ + 12m² + 8m. Second, complete the square on the left: 36k² + 36k + 24 is the same as 9(2k + 1)² + 15. Substituting Y for 2k + 1 and moving the 15 to the other side, the equation turns into
9Y² = 4m³ + 12m² + 8m − 15
A square on one side and a cubic on the other: that’s the signature of an elliptic curve, a type of equation mathematicians have studied for well over a century and have strong tools for. Those tools expect a standard form in which the square and the cube stand alone, without the 9 and the 4 in front. A rescaling achieves that. Multiply both sides by 11664, chosen because 9 × 11664 = 324² and 4 × 11664 = 36³, and set X = 36m, V = 324Y:
V² = X³ + 108X² + 2592X − 174960
Nothing has been lost along the way: every solution of the orange puzzle gives a lattice point on this curve, meaning a point whose coordinates X and V are both whole numbers. The name comes from picturing the whole-number points of the plane as a regular grid, or lattice. So if we can list all lattice points on the curve, we have caught every solution of the puzzle.
The curve V² = X³ + 108X² + 2592X − 174960 near the first three solutions. Two more solutions (4960 and 428536 oranges) lie far off to the right, at X = 1080 and X = 4896.
The chart shows the part of the curve near the first three solutions. Only lattice points matter. Because the equation only involves V², and −V squared is the same as V squared, the full curve also has a mirror image below the horizontal axis, where V is negative. The chart leaves it out because a negative V would mean a negative number of rings, so we have no use for those points.
To see how a point on the chart relates to oranges, take (72, 972). Plugging X = 72 and V = 972 into the equation gives 944784 on both sides, and that is what it means for the point to be on the curve. The number of oranges comes from translating back. Since X = 36m, we get m = 72 ÷ 36 = 2 layers, and a pyramid with 2 layers has 4 oranges. Likewise, since V = 324Y, we get Y = 972 ÷ 324 = 3, and Y = 2k + 1 then gives k = 1 ring, which is also 4 oranges.
The proof: all lattice points on the elliptic curve
Step 1: only finitely many lattice points exist. This is Siegel’s theorem, which Carl Ludwig Siegel proved in his 1929 paper Über einige Anwendungen diophantischer Approximationen. It holds for every elliptic curve. Since our curve is elliptic, the list of solutions must end. But the theorem only says that the list is finite, not what is on it. The work is in finding all of them.
Step 2: determine the rank of the curve. A rational point is one whose coordinates are fractions. Two rational points on an elliptic curve can be combined into a third by a geometric rule: draw the line through them and see where it meets the curve again. Mathematicians call this process ‘adding’ points.1 On our curve, for example, the line through the points for 1 and 4 oranges meets the curve again exactly at the point for 10 oranges, as the chart below shows.2 All rational points can be built this way from a few ‘generator’ points, and the number of generators needed is the curve’s rank (the Mordell–Weil rank). We need the generators because step 3 starts from them. A standard method called 2-descent shows this curve has exactly rank 2. Two independent programs (mwrank and PARI/GP) agree, and mwrank reports the result as certain rather than conditional.
The straight line through the points for 1 and 4 oranges meets the curve again at the point for 10 oranges. At this scale, the dashed line and the curve are almost on top of each other.
The gap between the curve and the line, magnified. The curve crosses the line at exactly the three solutions.
Step 3: find every lattice point. Every lattice point is also a rational point, so it is some combination of the generators. The question is which combinations land on lattice points. There’s a known algorithm for answering it, published in 1994 by Stroeker and Tzanakis and, independently, by Gebel, Pethő, and Zimmer. It uses a theoretical upper bound on how large such a point can be (from the theory of linear forms in elliptic logarithms, which extends Baker’s method to elliptic curves), shrinks that enormous bound with a technique called LLL reduction, and then checks the small remaining range directly. The implementation in SageMath finds exactly 14 lattice points on this curve.
Step 4: translate back to oranges. Not all 14 lattice points correspond to an arrangement of oranges. To get back from a point to layers and rings, we divide: m = X ÷ 36 and Y = V ÷ 324. A point only counts if both divisions come out as whole numbers. For example, (304, 6220) is on the curve, but 304 ÷ 36 is about 8.4, and a pyramid can’t have 8.4 layers. Ten of the 14 points pass. They come in mirror pairs (V and −V), because squaring removes the sign. The points with negative V would mean a negative number of rings, so we drop them like we did above, which leaves five solutions. Recovering k from Y = 2k + 1, these are
| Number of oranges | k (rings) | m (pyramid layers) |
|---|---|---|
| 1 | 0 | 1 |
| 4 | 1 | 2 |
| 10 | 2 | 3 |
| 4960 | 57 | 30 |
| 428536 | 534 | 136 |
Those are the five known numbers. Since every solution of the puzzle must appear among the 14 points, there is no sixth. A307491 is finite and complete.
Second result: OEIS A307492 (centered square and square pyramidal) is complete, too
Now let’s replace triangles with squares. Take 5 oranges. You can lay them flat as a square with one in the middle. Or you can build a square pyramid: four on the bottom, one on top.

Which numbers are both a centered square number, 2k² + 2k + 1, and a square pyramidal number, m(m + 1)(2m + 1)/6? The same steps work. Setting the two formulas equal, multiplying by 12 and completing the square with Y = 2k + 1 gives 6Y² = 4m³ + 6m² + 2m − 6. Rescaling with X = 24m, V = 144Y then gives the elliptic curve
V² = X³ + 36X² + 288X − 20736
It also has rank 2 and 14 lattice points. Filtering them as in step 4 leaves these solutions:
| Number of oranges | k (rings) | m (pyramid layers) |
|---|---|---|
| 1 | 0 | 1 |
| 5 | 1 | 2 |
| 42925 | 146 | 50 |
| 1026745 | 716 | 145 |
So A307492 is complete: 1, 5, 42925, 1026745. Its OEIS entry had the same open ‘greater than 1029’ comment.
Reproduce it yourself
You don’t have to take the software steps on faith. Anyone can re-run them in SageMath, which is free. The following lines define the first curve (the one for flat triangles and triangular pyramids, A307491), compute its rank, and list its lattice points. The numbers in the first line are the coefficients of the curve’s equation.
E = EllipticCurve([0, 108, 0, 2592, -174960])C = E.mwrank_curve()print(C.rank(), C.certain())# 2 Trueprint(E.integral_points(both_signs=True))# [(36 : -324 : 1), (36 : 324 : 1), (72 : -972 : 1), (72 : 972 : 1),# (108 : -1620 : 1), (108 : 1620 : 1), (304 : -6220 : 1), (304 : 6220 : 1),# (477 : -11583 : 1), (477 : 11583 : 1), (1080 : -37260 : 1), (1080 : 37260 : 1),# (4896 : -346356 : 1), (4896 : 346356 : 1)]
With SageMath 10.8, the first line of output is “2 True”, meaning rank 2, proven. The second line lists the 14 lattice points. SageMath writes each one in the form (X : V : 1). In ordinary notation, they are (36, ±324), (72, ±972), (108, ±1620), (304, ±6220), (477, ±11583), (1080, ±37260) and (4896, ±346356). Checking that a given point lies on the curve only takes a calculator. The software is needed to be sure that there are no others.
For the second curve (the one for flat squares and square pyramids, A307492), replace the list in the first line with [0, 36, 0, 288, -20736]. The output is again “2 True”, and the 14 lattice points are (24, ±144), (36, ±288), (48, ±432), (85, ±937), (468, ±10512), (1200, ±42192) and (3480, ±206352).
A related search: the same number in several dimensions
A flat triangle and a pyramid belong to one family. There are also ‘triangles’ in four, five and more dimensions, and counting their oranges gives the numbers in Pascal’s triangle. That raises a similar question: which numbers show up in Pascal’s triangle in several different places? Its mirror symmetry makes most numbers show up at least twice. Showing up more often than that is rare: apart from one infinite family, only a few such numbers are known, such as 120 and 3003. How often a number can repeat like this is a famous open problem called Singmaster’s conjecture.
A 2017 paper by Blokhuis, Brouwer and de Weger lists the known repeats, checked up to 1060. It also lists the known pairs of such numbers that differ by exactly 1, like 252 and 253, checked up to 1030. Claude extended both checks to 1070 and found nothing new. Unlike the two results above, this is a search and not a proof, so it says nothing about numbers beyond 1070.
How much to trust these findings
- The whole thing was produced by my AI, not by me, and no mathematician has reviewed it yet. The advent of competent AI calls for some new etiquette so unqualified people like me can report potential discoveries to experts without wasting their time. I’m not aware of any such etiquette, so I decided to blog about the potential discovery first. As a result, I had to strike a balance between making the article accessible to a general audience while also speaking to mathematicians. Anyone with SageMath or Magma can recompute the lattice points of the two curves above in seconds (those programs call them ‘integral points’).
- As shown above, these proofs are computer-assisted. Steps 2 and 3 are standard, widely used algorithms, but I’m trusting their implementations.
- I’m not a mathematician. I was good at undergrad computer-science and business-school math but didn’t go beyond that. And that was a while ago. That said, I kept asking my AI questions about the math until I understood it, and I ran the SageMath script to reproduce the results. AI wrote this article but I heavily edited it.
- As a further check, my AI ran the same code on two related puzzles whose complete answers are already in the literature: square tetrahedral numbers (A003556) and numbers both triangular and square pyramidal (A039596). It reproduced both lists exactly.
- My AI searched for an earlier proof and found none, but I can’t rule out that one exists somewhere. (For what it’s worth, both OEIS entries are still open at the time of writing, and web and arXiv searches on both sequences’ terms together with ‘elliptic curve’ turned up no proofs.)
Strictly speaking, the ‘sum’ of two points is the mirror image of that third point across the horizontal axis: same X, opposite V. The mirror image of a lattice point is again a lattice point, so this detail doesn’t affect the argument. ↑
That whole-number result is an exception. Usually the new point has fractions as coordinates. For example, combining the 4-orange point with itself (using the line that just touches the curve there) leads to a point with X = 436/9. This is why lattice points are so rare. ↑
Explanatory knowledge is the most potent force in the universe
#6058·Dennis HackethalOP, 19 days agoMaybe one day, when Veritula makes enough to pay for this feature.
It’s cheap. Claude says Haiku could do this for fractions of a cent. Opus for roughly 20 cents per discussion summary.
Veritula for Programmers

Programmers already know the rule Veritula runs on. A pull request with unresolved review comments shouldn’t get merged. On Veritula, an idea is rationally adoptable only if it has no pending criticisms, and a criticism is pending if it has no pending criticisms itself. Code is an idea like any other, so you can post it, criticize it, and improve it here.
GitHub and GitLab only go so far with that rule, though. You can comment on a gist, but not on a line of it. You can comment on a line of a pull request, but not on half of one. And a thread ends when someone clicks ‘Resolve conversation’, whether or not anyone answered it. Replies in GitHub discussions can’t even be deeply nested (more on that below). Veritula, on the other hand, is built around the simple rule above. Here’s what that means for your code.
Post a snippet, beautifully highlighted
Veritula doesn’t host repositories, at least not yet. It’s for snippets: the function, pattern, or fix you want criticized, with your reasons around it. Unlike GitHub gists, Veritula snippets can be surrounded by prose. Write a sentence or two, put your code between three backticks, name its language, and post. Veritula highlights over 200 languages, numbers the lines, and adds a button to copy the code. Here’s a function to try it on:
// Calls `call` until it succeeds, waiting twice as// long after each failureasync function retry(call, attempts = 5, delay = 500) {for (let attempt = 0; attempt < attempts; attempt++) {try {return await call();} catch (error) {if (attempt === attempts - 1) throw error;await sleep(delay * 2 ** attempt);}}}function sleep(ms) {return new Promise(resolve => setTimeout(resolve, ms));}
The editor above has all the features you’ve come to know and love as a programmer. It colors code as you type, the same way the final page will: what you see is what you get. Tab indents and Shift-Tab outdents. Return keeps the line’s indentation, and indents further after an opening bracket, Ruby’s do, or Python’s colon. ⌘/ comments lines out, ⌥↑ and ⌥↓ move them, and pasted code lands at the caret’s indentation. Put your cursor next to a parenthesis or bracket and it highlights the matching one. What you write is saved as a draft as you go, and ⌘S saves it right away. Code you need to keep private can go in a private discussion, which costs a one-time USD 3.00.
Criticize code, with unmatched precision
GitHub ties a review comment to whole lines. That’s a rather blunt tool. On Veritula, you select exactly what you mean – one keyword, half an expression, or a passage across several lines – and choose ‘Criticize…’ from the menu that appears. Your criticism opens with that code quoted, in the same colors it has in the block, even if your selection starts in the middle of a string or a comment.
Once submitted, the passage is marked in the original: red for pending criticisms, gray for addressed criticisms and comments. Hover over a marked passage to see the replies that quote it; click it to go to them. Hovering over a quote does the reverse, and clicking it takes you back to the corresponding part of the code.
The retry function above, once three readers have replied: a pending criticism of how it catches errors, a comment, and an addressed criticism of its numbers.
No button resolves a criticism – the rule does, automatically
On GitHub and GitLab, a thread is resolved when someone clicks a button. Veritula has no such button. A criticism of your code stands until it’s addressed with a counter-criticism or a revision. That answer can be criticized in turn, as deep as the argument goes, and whether your code is rationally adoptable follows from the whole tree, worked out the same way at every level.
Review threads on GitHub are flat: every reply goes at the end of one list. But Veritula is deeply nested: every reply can get replies of its own, so two objections to the same answer don’t get tangled up with each other and criticisms are evaluated recursively.
Revisions that know what they addressed
When a line changes on GitHub, the comments on it are marked ‘Outdated’, whether the change addressed them or not. On Veritula, a revision is a new version, and you choose which replies still apply to it. Those carry over; the criticisms it addressed are credited in it by name, notifying their authors that their criticism led to the change. It’s like naming your reviewers in a commit message, except Veritula does it for you.
Anyone can compare the versions. For code, it’s like a git diff: the lines added and removed, the words that changed within them, and the unchanged lines folded away.
A revision addressing that criticism, compared with the version before it.
When criticisms pile up, a guide takes you through them one at a time. For each, you can address it, revise your idea, or move on for now.
Replay the argument
Each discussion is drawn as a tree beside the text, with pending criticisms in red. A timeline along the bottom of the page goes back through its history, one point for each idea and revision. Drag it back, and the page shows the discussion as it was then: the code before it was revised, and only the replies posted by that time.
The same discussion as a tree. Hollow ideas aren’t rationally adoptable: the retry function (blue) shown through several historical states, with and without pending criticisms.
Share it anywhere
Post a link to an idea with code on X, Slack, or iMessage, and its preview shows the code, highlighted:

Email notifications highlight code as well. Refer to another idea by its number, like #5114, and it links there. Your idea also shows up on the referred idea’s page, much like GitHub’s ‘mentioned this’ links.
Put money on it
Want more eyes on your code snippet? Offer a bounty for criticism, like a bug bounty: name an amount and your terms, and pay out for the criticisms you accept. Others can add their own funding to yours.
Bring your assistant
Veritula has an MCP endpoint at https://veritula.com/mcp. Add it to any coding agent that supports MCP, and it can search and read discussions. Once you sign in, it can also post ideas and replies for you. Everything it posts is properly disclosed as written with AI, automatically.
Try it here
This article is an idea like any other, so the rule applies to it too. If you think the retry function above is wrong, or anything else in this article, select it, choose ‘Criticize…’, and say why. Until someone addresses your criticism, this article isn’t rationally adoptable. The rule decides automatically. It is, in fact, simply expressed in the following code snippet:
def should_adopt? ideapending_criticisms(idea).none?enddef pending_criticisms ideacriticisms(idea).filter { |c| should_adopt?(c) }end
For more details and a discussion of this rule, see #5114:
def should_adopt? idea
pending_criticisms(idea).none?
end
def pending_criticisms idea
criticisms(idea).filter { |c| should_adopt?(c) }Big week on Veritula 🚀
✍️ A new editor: what you see is what you post.
📰 Long-form articles on your profile. Write one here.
🖼️ Pictures: drop or paste them into the editor; caption them.
💾 Drafts that save as you type and on cmd + s.
🙌 Revisions credit the critics who inspired them.
😎 Profile pictures. Set one here.
@moritz-wallawitsch has already posted his first article:

#5970·Moritz Wallawitsch, 26 days agoIn Defense of Ambition
Photo by Josh Behunin on Unsplash
Until about a week ago, when I debated this with my friend Tom Hyde, it seemed incredibly obvious to me that wanting to be rich is rational but I could not explain why. Fundamentally I agree with Popper and Deutsch that all life is problem solving. Throwing a birthday party is solving a problem, and so is stopping an asteroid that is heading toward Earth. But is not having a mansion on Lake Como a problem? Is not being immortal a problem? How do we decide between these?
The lone scientist obsessed with some theoretical problem strikes most people as weird. How can someone toil for 30 years on a problem, living on a modest salary, without any desire to accumulate massive wealth? According to the Popperian view above, that is completely rational. The scientist is fully engrossed in problem solving.
If that is true, why am I and most people so drawn to solving my problem of not having a mansion on Lake Como, driving a Bugatti hypercar, or overlooking the skyline of NYC from a Billionaires’ Row apartment? Status plays a part, but I think I’d want these things even if nobody knew about them.
I think the two reasons are:
- Wealth is the repertoire of things you can do. I can likely only experience these things with incredible wealth. They are a symbol of that repertoire. Some of my desires I know already, like the mansion. Most I don’t know yet. Future desires are unknowable, thus attaining wealth allows me to solve problems in the future I don’t know I’ll have.
- This is the antithesis to the famous fisherman story (a businessman advises a fisherman to scale up his business so he can retire and fish). The story contains the assumption that the fisherman already knows the problem he wants to solve. However, the fisherman might want to travel the world, build a school, research AI, explore space.
- Imagine a person 1000 years ago. They might only want better food, safer shelter, a healthy family, or perhaps a larger farm. But if they knew these things existed, they would desire flying across the Atlantic, playing table tennis, or listening to recorded music.
- There are clear limits to this theory. Just because I’m a septillionaire doesn’t mean I get a cure for cancer. The fundamental limitation remains the knowledge.
- Beauty is accumulated knowledge. These experiences—the lake mansion, the hypercar, the best view of the most awe-inspiring skyline humanity has forged—are objects of incredible beauty. They are products of accumulated human knowledge. So desiring beauty is a form of desire to experience knowledge. Deutsch argues beauty is objective and improves like science. But beauty doesn’t look like a problem we need to solve. Why do we seek it out?
So how ambitious should you be? In the startup world there is this famous saying that “Starting a company is always hard and it’s about equally hard no matter what you do.” Bigger problems are riskier, but a bigger vision lets you raise more money and hire better people, which reduces the risk. So why not solve the problem people pay the most for? Or should you only be guided by curiosity, as that drive likely carries some latent knowledge about what society wants or where the most interesting gaps are? I don’t know. I suspect it is best to follow a mix of these strategies. Whatever the right mix is, I don’t think ambition is bounded.
The child who grows up knowing they will inherit several large companies might aim to build an even bigger one, while the child of, say, a cashier might aim to be a cashier. Each is working from a conjecture about what is within reach, formed from what they see the people around them do. It only gets criticized if the child asks whether they could do what someone like Elon Musk did, or something nobody has done before. That is why optimism matters for ambition. It says the only limit is what we don’t yet know.
Alexandr Wang recently said “Humans will dream bigger and bigger. Kids will dream of having their own star systems.” I think that is true. And one day kids will dream of owning several galaxies. We don’t yet know what we’d want after that. Human dreams, desire, and our future are infinite.
#6096·Dennis Hackethal revised 11 days agoweb-haptics: Haptic Feedback Finally Comes to iOS Safari
Photo by Andrew Kliatskyi on Unsplash
lochie just published a package bringing haptic feedback to the web on iOS: web-haptics. I spent four years at Apple working mostly on UIs, so I pay close attention to design and UX. In terms of presentation, use case, and attention to detail, this package comes as close to perfect as I’ve seen.
The problem
On Android, haptics have been available through
navigator.vibrate(), but iOS does not support this API. There is a workaround, though – theinputelement with theswitchattribute:html<input type="checkbox" switch>See this switch input in action here. On iOS, the input renders not as a regular checkbox but as a switch that can be toggled on or off:
Open the link on your iPhone, tap the switch, and you’ll feel haptic feedback. As far as I know, this is currently the only known practical workaround to trigger haptic feedback on the web in iOS.
Implementation
lochie cleverly bases his package on this single workaround and pushes it to its limits. The key lines are here, creating a switch input on the fly:
js (rendered as javascript)const hapticCheckbox = document.createElement("input");hapticCheckbox.type = "checkbox";hapticCheckbox.setAttribute("switch", "");The package also creates an associated label and clicks it to trigger haptic feedback on the connected input:
js (rendered as javascript)this.hapticLabel.click();(My understanding is that iOS will not trigger the haptic feedback when the input is programmatically clicked – so the ‘workaround within the workaround’ is to click the associated label instead.)
Custom haptics (!)
But web-haptics can do more than just trigger a single haptic ‘pulse’. The
triggerfunction lets you pass in your own patterns with pulses of different duration and intensity, and even delays.Want a simple success haptic? Trigger two pulses, as shown on the package homepage:
js (rendered as javascript)trigger([{ duration: 30 },{ delay: 60, duration: 40, intensity: 1 },])Or simply call
trigger('success')as a shortcut.Here’s an error haptic I made based on Apple’s human interface guidelines:
js (rendered as javascript)trigger([{ duration: 40, intensity: 0.7 },{ delay: 40, duration: 40, intensity: 0.7 },{ delay: 40, duration: 40, intensity: 0.9 },{ delay: 40, duration: 50, intensity: 0.6 },])lochie’s website even gives you an editor to make custom haptics. As you click and drag, both pulses and code update in real time. This experience is developer bliss:
Usage
Importing the package is easy. In Rails, for example:
rb (rendered as ruby)# config/importmap.rbpin "web-haptics", to: "https://esm.sh/web-haptics@0.0.6"js (rendered as javascript)import { WebHaptics } from 'web-haptics';// In a Stimulus controller somewherelet haptics = new WebHaptics();haptics.trigger('success'); // or 'error', 'warning', etc for built-in hapticshaptics.trigger([{ duration: 40, intensity: 0.7 },{ delay: 40, duration: 40, intensity: 0.7 },])Pass a
debugoption to the constructor to hear clicks in development/on desktop:js (rendered as javascript)new WebHaptics({ debug: true });Again, this package comes as close to perfect as I’ve seen. In fact, I already use it on Veritula: as you type in a text field, an error haptic plays when you exceed the max length (#4473).
I highly recommend web-haptics by lochie.
Comes to iOS Safari
Sadly, as of a recent iOS update, Apple removed the ability to trigger haptic feedback in Webkit browsers.
Should still work on Android, though.
Add cover image
web-haptics: Haptic Feedback Finally Comes to iOS Safari
lochie just published a package bringing haptic feedback to the web on iOS: web-haptics. I spent four years at Apple working mostly on UIs, so I pay close attention to design and UX. In terms of presentation, use case, and attention to detail, this package comes as close to perfect as I’ve seen.
The problem
On Android, haptics have been available through navigator.vibrate(), but iOS does not support this API. There is a workaround, though – the input element with the switch attribute:
<input type="checkbox" switch>
See this switch input in action here. On iOS, the input renders not as a regular checkbox but as a switch that can be toggled on or off:

Open the link on your iPhone, tap the switch, and you’ll feel haptic feedback. As far as I know, this is currently the only known practical workaround to trigger haptic feedback on the web in iOS.
Implementation
lochie cleverly bases his package on this single workaround and pushes it to its limits. The key lines are here, creating a switch input on the fly:
js (rendered as javascript)const hapticCheckbox = document.createElement("input");hapticCheckbox.type = "checkbox";hapticCheckbox.setAttribute("switch", "");
The package also creates an associated label and clicks it to trigger haptic feedback on the connected input:
js (rendered as javascript)this.hapticLabel.click();
(My understanding is that iOS will not trigger the haptic feedback when the input is programmatically clicked – so the ‘workaround within the workaround’ is to click the associated label instead.)
Custom haptics (!)
But web-haptics can do more than just trigger a single haptic ‘pulse’. The trigger function lets you pass in your own patterns with pulses of different duration and intensity, and even delays.
Want a simple success haptic? Trigger two pulses, as shown on the package homepage:
js (rendered as javascript)trigger([{ duration: 30 },{ delay: 60, duration: 40, intensity: 1 },])
Or simply call trigger('success') as a shortcut.
Here’s an error haptic I made based on Apple’s human interface guidelines:
trigger([{ duration: 40, intensity: 0.7 },{ delay: 40, duration: 40, intensity: 0.7 },{ delay: 40, duration: 40, intensity: 0.9 },{ delay: 40, duration: 50, intensity: 0.6 },])
lochie’s website even gives you an editor to make custom haptics. As you click and drag, both pulses and code update in real time. This experience is developer bliss:

Usage
Importing the package is easy. In Rails, for example:
# config/importmap.rbpin "web-haptics", to: "https://esm.sh/web-haptics@0.0.6"
import { WebHaptics } from 'web-haptics';// In a Stimulus controller somewherelet haptics = new WebHaptics();haptics.trigger('success'); // or 'error', 'warning', etc for built-in hapticshaptics.trigger([{ duration: 40, intensity: 0.7 },{ delay: 40, duration: 40, intensity: 0.7 },])
Pass a debug option to the constructor to hear clicks in development/on desktop:
new WebHaptics({ debug: true });
Again, this package comes as close to perfect as I’ve seen. In fact, I already use it on Veritula: as you type in a text field, an error haptic plays when you exceed the max length (#4473).
I highly recommend web-haptics by lochie.
web-haptics: Haptic Feedback Finally Comes to iOS Safari
Photo by Andrew Kliatskyi on Unsplash
lochie just published a package bringing haptic feedback to the web on iOS: web-haptics. I spent four years at Apple working mostly on UIs, so I pay close attention to design and UX. In terms of presentation, use case, and attention to detail, this package comes as close to perfect as I’ve seen.
The problem
On Android, haptics have been available through navigator.vibrate(), but iOS does not support this API. There is a workaround, though – the input element with the switch attribute:
<input type="checkbox" switch>
See this switch input in action here. On iOS, the input renders not as a regular checkbox but as a switch that can be toggled on or off:

Open the link on your iPhone, tap the switch, and you’ll feel haptic feedback. As far as I know, this is currently the only known practical workaround to trigger haptic feedback on the web in iOS.
Implementation
lochie cleverly bases his package on this single workaround and pushes it to its limits. The key lines are here, creating a switch input on the fly:
js (rendered as javascript)const hapticCheckbox = document.createElement("input");hapticCheckbox.type = "checkbox";hapticCheckbox.setAttribute("switch", "");
The package also creates an associated label and clicks it to trigger haptic feedback on the connected input:
js (rendered as javascript)this.hapticLabel.click();
(My understanding is that iOS will not trigger the haptic feedback when the input is programmatically clicked – so the ‘workaround within the workaround’ is to click the associated label instead.)
Custom haptics (!)
But web-haptics can do more than just trigger a single haptic ‘pulse’. The trigger function lets you pass in your own patterns with pulses of different duration and intensity, and even delays.
Want a simple success haptic? Trigger two pulses, as shown on the package homepage:
js (rendered as javascript)trigger([{ duration: 30 },{ delay: 60, duration: 40, intensity: 1 },])
Or simply call trigger('success') as a shortcut.
Here’s an error haptic I made based on Apple’s human interface guidelines:
trigger([{ duration: 40, intensity: 0.7 },{ delay: 40, duration: 40, intensity: 0.7 },{ delay: 40, duration: 40, intensity: 0.9 },{ delay: 40, duration: 50, intensity: 0.6 },])
lochie’s website even gives you an editor to make custom haptics. As you click and drag, both pulses and code update in real time. This experience is developer bliss:

Usage
Importing the package is easy. In Rails, for example:
# config/importmap.rbpin "web-haptics", to: "https://esm.sh/web-haptics@0.0.6"
import { WebHaptics } from 'web-haptics';// In a Stimulus controller somewherelet haptics = new WebHaptics();haptics.trigger('success'); // or 'error', 'warning', etc for built-in hapticshaptics.trigger([{ duration: 40, intensity: 0.7 },{ delay: 40, duration: 40, intensity: 0.7 },])
Pass a debug option to the constructor to hear clicks in development/on desktop:
new WebHaptics({ debug: true });
Again, this package comes as close to perfect as I’ve seen. In fact, I already use it on Veritula: as you type in a text field, an error haptic plays when you exceed the max length (#4473).
I highly recommend web-haptics by lochie.
#4509·Dennis Hackethal, 8 months agoweb-haptics: Haptic Feedback Finally Comes to iOS Safari
lochie just published a package bringing haptic feedback to the web: ‘web-haptics’. I spent four years at Apple working mostly on UIs, so I pay close attention to design and UX. In terms of presentation, use case, and attention to detail, this package comes as close to perfect as I’ve seen.
The problem
On Android, haptics have been available through
navigator.vibrate(), but iOS does not support this API. There is a workaround, though – theinputelement with theswitchattribute:html<input type="checkbox" switch>See this switch input in action here. On iOS, the input renders not as a regular checkbox but as a switch that can be toggled on or off:
Open the link on your iPhone, tap the switch, and you’ll feel haptic feedback. As far as I know, this is currently the only known practical workaround to trigger haptic feedback on the web in iOS.
Implementation
lochie cleverly bases his package on this single workaround and pushes it to its limits. The key lines are here, creating a switch input on the fly:
js (rendered as javascript)const hapticCheckbox = document.createElement("input");hapticCheckbox.type = "checkbox";hapticCheckbox.setAttribute("switch", "");The package also creates an associated label and clicks it to trigger haptic feedback on the connected input:
js (rendered as javascript)this.hapticLabel.click();(My understanding is that iOS will not trigger the haptic feedback when the input is programmatically clicked – so the ‘workaround within the workaround’ is to click the associated label instead.)
Custom haptics (!)
But web-haptics can do more than just trigger a single haptic ‘pulse’. The
triggerfunction lets you pass in your own patterns with pulses of different duration and intensity, and even delays.Want a simple success haptic? Trigger two pulses, as shown on the package homepage:
js (rendered as javascript)trigger([{ duration: 30 },{ delay: 60, duration: 40, intensity: 1 },])Or simply call
trigger('success')as a shortcut.Here’s an error haptic I made based on Apple’s human interface guidelines:
js (rendered as javascript)trigger([{ duration: 40, intensity: 0.7 },{ delay: 40, duration: 40, intensity: 0.7 },{ delay: 40, duration: 40, intensity: 0.9 },{ delay: 40, duration: 50, intensity: 0.6 },])lochie’s website even gives you an editor to make custom haptics. As you click and drag, both pulses and code update in real time. This experience is developer bliss:
Usage
Importing the package is easy. In Rails, for example:
rb (rendered as ruby)# config/importmap.rbpin "web-haptics", to: "https://esm.sh/web-haptics@0.0.6"js (rendered as javascript)import { WebHaptics } from 'web-haptics';// In a Stimulus controller somewherelet haptics = new WebHaptics();haptics.trigger('success'); // or 'error', 'warning', etc for built-in hapticshaptics.trigger([{ duration: 40, intensity: 0.7 },{ delay: 40, duration: 40, intensity: 0.7 },])Pass a
debugoption to the constructor to hear clicks in development/on desktop:js (rendered as javascript)new WebHaptics({ debug: true });Again, this package comes as close to perfect as I’ve seen. In fact, I already use it on Veritula: as you type in a text field, an error haptic plays when you exceed the max length (#4473).
I highly recommend web-haptics by lochie.
Superseded by #4510.
Be more specific about what ‘having programmed it’ means
Preference formation is a computational task. (#5255) To claim we’ve understood a computational task, we must have programmed it (Deutsch). Popper never programmed it, so he didn’t understand it.
(To his credit though, he got pretty technical in defining verisimilitude, logical vs. empirical content, etc.)
Preference formation is a computational task. (#5255) To claim we’ve understood a computational task, we must have programmed it (Deutsch). Popper never programmed it, so he didn’t understand it. (To Popper’s credit though, he got pretty technical in defining verisimilitude, logical vs. empirical content, etc.)
Satisfying Deutsch’s yardstick means, in my professional opinion as a software engineer: writing working code to industry standards. It must compile, implement the desired feature with no known bugs, be as performant as the feature reasonably requires, have clean syntax, etc.
#6078·Moritz Wallawitsch, 16 days ago·AI-assistedThis rules out partial understanding. I understand aspects of how capitalism works even though I couldn’t build a faithful simulation of a capitalist economy. Not having programmed preference formation doesn’t show that Popper understood nothing about it. What specific part did he fail to understand?
Not having programmed preference formation doesn’t show that Popper understood nothing about it.
#5256 doesn’t say Popper understood nothing about it. I think he understood it fairly well, far better than other philosophers, but we now understand it better today than he did.
#6078·Moritz Wallawitsch, 16 days ago·AI-assistedThis rules out partial understanding. I understand aspects of how capitalism works even though I couldn’t build a faithful simulation of a capitalist economy. Not having programmed preference formation doesn’t show that Popper understood nothing about it. What specific part did he fail to understand?
I understand aspects of how capitalism works
Capitalism isn’t a computational task.
#6080·Moritz Wallawitsch, 16 days ago·AI-assistedHow do we decide which test is more severe? That judgment seems to do the work of forming the preference here. The criticism tree records the judgment, but what explains how we reach it?
Again I think it depends on the test. I don’t think it’s a fundamental problem for the theoretician, more so for the applied epistemologist. But in general terms, you can just guess that one is more severe, and if nobody has a pending criticism of that guess, you can rationally go ahead with it.
Dennis Hackethal updated discussion ‘Sleep’.
The ‘About’ section changed as follows:
Sleep is a challenge for many, including me. Let's see what we know.
Sleep is a challenge for many, including me. Let's see what we know.
No medical advice, follow at your own risk.
I purposely avoid drinking any water past 8pm so I don’t wake up in the middle of the night having to answer nature’s call. But sometimes that still happens. How can that be?
According to this article, elevating your legs helps with fluid distribution. My understanding is that, as you get older, your body gets worse at fluid distribution. If you’re never horizontal during the day, fluid gathers in your legs. And if bedtime is the first time you’re horizontal, the fluid redistributes as you sleep, some of it gathers in your bladder, and you need to pee.
That explains why I had to use the bathroom in the middle of the night even though I don’t drink anything past 8pm. So I tried elevating my legs the next day, working from the couch for a bit in the evening. It worked! I didn’t wake up having to use the bathroom that night. (I still woke up for some other unidentified reason, but at least I can now rule out this cause.)
#6087·Moritz WallawitschOP, 15 days ago·AI-assistedShort videos prevent the depth of explanation that long-form content can provide.
They can introduce subjects worth exploring further. Not every useful encounter needs to be exhaustive.
#6082·Moritz WallawitschOP, 16 days ago·AI-assistedPersonalized short-video feeds such as TikTok, Reels and YouTube Shorts are better at serving curiosity than fixed, long-form films. They adapt to your interests instead of fixing a sequence.
Short videos prevent the depth of explanation that long-form content can provide.
#6085·Moritz WallawitschOP, 15 days ago·AI-assistedPeople sometimes keep watching these feeds while also wanting to stop. That conflicts with the claim that the feed is serving their interests.
Which conflicting preferences are involved? Why assume stopping expresses the person’s real preference, rather than treating both preferences as ideas to examine?
#6082·Moritz WallawitschOP, 16 days ago·AI-assistedPersonalized short-video feeds such as TikTok, Reels and YouTube Shorts are better at serving curiosity than fixed, long-form films. They adapt to your interests instead of fixing a sequence.
People sometimes keep watching these feeds while also wanting to stop. That conflicts with the claim that the feed is serving their interests.
#6083·Moritz WallawitschOP, 16 days ago·AI-assistedRecommendation engines optimize engagement and time spent, not curiosity. Engagement is not always curiosity.
True, but serving curiosity is one way to sustain engagement. What separates engagement that serves curiosity from engagement that does not?