Red Blob Games' tutorial on draggable object implementation is technical educational content that advocates for freely shared knowledge about web development and interactive design. The material is strongly aligned with the right to education (Article 26) through comprehensive, progressive documentation with working examples, and supports freedom of expression (Article 19) and cultural participation (Article 27) by teaching tools and techniques for interactive creative systems.
Amit was an instrumental part in the development of one of my favorite video games, Realm of the Mad God. It was a masterpiece of the Flash game genre, and its guild feature introduced me to many lifelong friends.
I remember when I got back into programming, this site was one of the things that really made me excited to code + develop a deeper understanding of algorithms :)
Off-topic a bit, but I've been curious about a 2D pathfinding problem for a while that this site doesn't seem to tackle despite having lots of articles on the subject. Is there an algorithm out there for finding "enclaves" (i.e. places where you might want to place rewards, spawn the player) within a large 2D terrain grid?
Not super precise, but given a 2D boolean array of pathable/unpathable cells, say generated by Perlin noise, find locations that are only accessible via a relatively narrow "choke point". Example: https://imgur.com/a/jFPXlS5
Standard pathfinding algorithms don't provide enough information to do this, but maybe there's some kind of heuristic approach that could work well.
Apart from the excellent subject matter, I often pull up this site during UI/UX discussions. Amit clearly has the ability to do really advanced JavaScript visualizations, but he only uses it exactly when necessary. Most of it is a plain document like you might write in Markdown, but when he uses JavaScript, it's illuminating, connected to all the other examples, and clean. Any animation he uses is clearly initiated by the user, and is there not because it looks cool, but because the intermediate frames help the user understand what's happening. It also never moves the rest of the layout around. I go back to this site any time I'm pondering how to do good online documentation, interactive help, tutorials, or even text-heavy presentation of results.
Am going to chime in as well - Amit is amazing and has been providing his knowledge and experience since the early 90's, when I first encountered him on FidoNet over BBS, then next on Usenet.
A fantastic site. When I originally took over teaching Intro to AI, I initially relied on the A* search closed/open set pseudocode explanation[1]. However, when it would come time to ask students to implement it, I was constantly finding students absolutely confused by the approach. Once I swapped over to Amit's A* explanation, the number of confused students dropped significantly. Forever thankful for their walkthrough.
I owe my thanks to this site too. When my team was designing our hexagonal system for geographical analysis back in Uber, I referred to https://www.redblobgames.com/grids/hexagons/ a lot.
This article is about dragging, and I've run into all of the pitfalls and come to the same solutions that Amit talks about. Excellent article!
One of the hardest things I have needed to code from scratch is drag-to-reorder. It seem so natural from a user perspective, but when you get into inconsistently sized items, having to create placeholders between items, detecting edges, going down rabbitholes of box-fitting algorithms... it's a fun challenge :)
One extra detail, something I've learned from 20 years of working on dragging all kinds of objects around the GUI of Ardour [0]: handle ALL button press and release events as drag events where there is no movement. That is: press ALWAYS starts a drag that is finished by the release, and the code that handles release "special cases" the no-movement condition.
In the distant past, there was this NSF-funded geometry center in Minneapolis. There was a computational group theory conference (these guys are over the top) and they invited a few mascots from neighboring fields. I had written a system for algebraic geometry, and got an invite. I'd get up really early in the -20F cold to insure a Silicon Graphics workstation for the day, and set about coding a game to better understand group generators and relations.
It involved dragging.
I loved the 2am conversations that resulted. My idea was that dragging need not respect real-world physics. Dragging should feel like a great tab of acid. And everyone was into this, everyone had ideas.
I was just implementing dragging an SVG element in a Vue app earlier this week, and had to discover pretty much everything the author describes in the article, even in the same order the author describes them, and ended up with pretty much an identical component to do so (except I wrote a composable utility `useDragging` instead of a functional component `<Draggable>`).
The article implements relatively basic dragging (notwithstanding the several edge cases that arise with web browsers). Are there resources on dragging with constraints such as snapping to guidelines, preventing collisions with boundaries or other objects, or animated drop targets that resize or move in response to the drag operation?
I once wanted to make a customizable Pomodoro timer UI based on subdividing a circular clock into wedges of different durations to define your focus/break intervals. I didn't get very far trying to implement drag-to-reorder of the wedges.
The article doesn’t seem to discuss cancellation. For example, there is the convention (at least on Windows) that pressing Escape cancels the dragging. Sometimes you also want to cancel the dragging when the mouse-up happens outside of some defined area. Cancellation serves as a quicker Undo (or an “oh, I actuality didn’t mean to drag”) for the user. In any case, this means that you have to save the original state at the start of the dragging, so that it can be restored if the dragging is cancelled, even if you otherwise provide no Undo functionality.
In the case of cancellation-when-dragging-outside-an-area, there’s also un-cancellation, meaning you resume the dragging when the pointer returns to the area, after the state visually reverted to the original one while outside the area (to indicate to the user that a cancellation would happen if the mouse button is released at that point). Or put differently, the real cancellation only happens upon mouse-up, but is already visually indicated while dragging.
RotMG has a great idea that I wish more games would copy: difficulty scales with elevation. If you want to take it easy, stick to the coasts. If you want a challenge, strike inland towards the mountains (or follow a river upstream). It's a great way of intuitively expressing difficulty ranges across a sprawling world map, and I remember being disappointed the first time that I played Skyrim that it didn't seem to do the same.
Lots of pathfinding solutions prefer to work with connected convex polygons (since inside the polygon, you can always go straight to every other point inside). You could merge your cells into these polygons and then filter for small ones by area, I guess.
You are not trying to find a "path" since you don't have a starting point - you only have end points. Your enclaves are more defined by the shape of the walls (roughly x cells in radius) than with the fact that you reach them through chokepoints. After all, a chokepoint can be just a small door in an otherwise big corridor, separating two big areas. And by concentrating on chokepoints you will lose all the "isolated islands".
You might be luckier treating this as a "map treatment" problem. An algorithm that does things to the whole map, and then reads the result.
For example:
1. Start assigning a score of 0 to all map cells.
2. For every cell in the map, set to 1 if it's in contact with any walls
3. Then add 1 to every cell of the map if it contacts a cell with a non-zero value
4. Repeat the above step n times, where n is the average "radius" of you enclave rooms.
5. Every cell with a score of n or higher is a "candidate". For every candidate:
5.a Check that none of the cells around have a bigger score. If so, move on to the next candidate
5.b Check that there's no "treasure" around it in a circle of radius n
5.c You have found the center of an enclave. Mark it with "treasure" and move on to the next cell.
Find starting point in dungeon. A* to every room. Rooms with a large distance (iter count) and with only one or two paths out (choke points, use graph to see edges) become potentials, every potential that is _far & narrow_ is flagged “enclave” with rewards increasing by distance to start.
This is one way to approach the problem. The other way is to do prefab rooms and when generating your dungeons, randomly select one or two prefab enclave rooms to throw into the shuffle. Shuffle the rooms and spread them out then connect hallways and such. This is the approach that Enter the Gungeon took.
Another approach is what @otikik describes. Tracing the walls buy assigning a value to the cells that can then be scored. Minesweeper style.
Tarjan's Algorithm can be used to find choke points, although I haven't tried it myself [1]. I think what I would try is Breadth First Search with multiple start points to calculate various metrics for the map [2] and maybe All-Pairs if you need more [3]. For example, you might use Tarjan's or All-Pairs to find the most commonly used corridors, and then use Breadth First Search from those "central" points to find the "farthest from central corridor" points.
That is a fantastic discussion on dragging with Javascript. Really appreciate the information.
I've a quick question. How do you restrict the dragging movement to an axis using the built-in DOM events like dragstart/etc. I had a drag & drop feature implemented using the dragstart/dragenter/dragover/drop/etc events. I couldn't find a quick way to restrict the dragging movement to the x-axis. JQuery's drag and drop API used to support it. I'm trying to use the native DOM events/api only. Any information or pointers are greatly appreciated.
I always wish there was more specification surrounding his Perlin noise algorithm he has on his website. For example, asking what the range of output values is does not have an easy answer.
Thank you! I treat them as documents, with some interactive elements. I tend to avoid traditional animations because they're outside the reader's control. I want the reader to be able to pause, slow down, speed up, rewind, etc. One thing I'm going for but haven't completely achieved is that I want the diagrams to be useful even before the interaction.
Overwhelmingly, the answer is "you don't." Your UI should provide alternatives to dragging that allow folks who can't use a point/touch devices to interact with your page. Which is to say, don't shoehorn keyboard support into your drag implementation, add separate keyboard functionality that makes sense in addition to drag functionality.
I have a trick for this that I love that is very general:
1. When a user begins dragging, calculate the layouts for all possible drop targets (or perhaps just those that are currently visible).
2. For each of those layouts record the position the dragged objects ends up in.
3. On each mouse movement, select from those positions the one that's closest to the dragged object's current position
4. Render the selected layout
This ends up feeling really good and works for any kind of complex layout / reflow.
I had to tackle this problem for my index card app, Card Buddy. [1] It was definitely a fun challenge and I still found a better way to do it later.
What I ended up doing is when you pick up a card, I compute the layout as if the card was deleted from the board, and then it becomes easy. Wherever you hover the mouse, I just displace whatever is there.
There were still tons of edge cases I had to work out, though, especially when you start editing a new card that hasn’t been “committed” to the data model yet. I had to add the option to shift existing cards out of the way to make room for a phantom card.
It helps to recognize that there are just lots of edge cases you have to manually handle. If you try to tackle it as though there’s a more generic/homogeneous solution, you end up going around in circles a bit with the design. I should probably create a blog post on all the different edge cases.
As I said, though, I found an even better way to do my layout, which saves on unnecessary computations and makes the layout engine more flexible and user-friendly. (It’s amazing what a difference your choice of data model representation makes on your solution.) It’s been a fun puzzle to solve!
I think that's a bit off base here. It's like asking how does a keyboard user facilitate click-and-hold to pan the canvas in a drawing program? They don't. Panning with the mouse (or dragging objects) is one interface. The task is panning the canvas, the means to achieve that are varied.
In my example, the answer to the question about accessibility is to additionally provide keyboard controls to complete the task (preferably ones that don't - or optionally don't - require holding a key). For example perhaps a key shortcut to enter a pan mode, in which the arrow keys move the viewport around the canvas. Problem solved.
As for draggable objects, the task is re-ordering. How do you make that accessible? Provide an alternative means to re-order objects, perhaps using TAB to cycle focus through the objects, then a key to select the focused one, and use the arrow keys to move the drag preview to the nearest valid position in that direction.
That should also work, but I did not do it this way and had no problems after I got the basics right. I have general mousedown and mouseup handlers and use a timeout(~150 ms, but configurable), to determine if we deal with a click, or start dragging (or other things).
And on mouseup (or if the mouse leaves the screen) and there is dragging in progress -> stopdrag. So dragging for me ist just one of different special cases ..
I used to write browser UI code all the time, but it’s been a few years. This is the first I’ve heard of someone using pointer events.
Do you worry at all about someone’s browser not supporting them, given that Safari added support in 2020? I guess Safari 12 is hopefully no longer used in practice, with macOS Mojave users hopefully running Safari 13 or 14? It would be pretty bad if something as simple as dragging didn’t work in a production app designed for the market of web users at large.
Adding event handlers to the document during a drag is a time-honored practice, and browsers add brand-new features all the time that are intended to simplify some use case or other but have their own edge cases and gotchas, which the article says are not fully addressed. And there’s still a combination of pointer and touch events in the end result. I wonder if the “simplicity” in the sense of less code is worth the additional edge cases, less browser support, and the developer needing to understand the ins and outs and browser differences of pointer events, which are presumably less understood and documented than mouse events.
The way this code works, the drag motion updates some state. I can apply constraints when setting the state. Then the state drives the redisplay.
I wanted to separate the constraint system from the event handling system. Libraries like jquery-ui tie them together, so the event handling system has to know all the possible constraints. In jquery-ui, they support bounding box, axis, square grid, snapping to dom elements. But what if I wanted snapping to a hex grid, or a logarithmic scale grid, or a bounding circle? It's not supported.
In the code you'll see "state.pos = …". That's where the state is set. For constraints, I put "pos" behind a setter. Then the setter can apply the constraint, without the drag event handling code having to know what type of constraints are needed.
I should update the page to show some examples of constraints. I completely forgot to mention this aspect of the code recipe. (Thanks!)
Cool! I've tried a directive and tried a component (with slots) but I haven't tried a composable, mainly because I wasn't sure how to set up event handlers that way. (I don't have a lot of experience with composables)
Content is comprehensive educational material teaching web development techniques. Structured as progressive tutorial from basic to advanced concepts. Includes historical context, best practices, and multiple working examples demonstrating each technique.
FW Ratio: 63%
Observable Facts
Page contains structured tutorial with 5 major sections progressing systematically from simple mouse event handling to advanced pointer event techniques.
Content includes interactive code examples demonstrating each technique that readers can experiment with directly.
Live demos provided for each major concept, allowing hands-on experimentation and verification of understanding.
Content is freely accessible without login, subscription, paywall, or other access barriers.
Author provides historical context showing evolution of web standards from 2011-2020, supporting learner understanding of why current techniques are preferred.
Inferences
Progressive structure from basic to advanced demonstrates intentional educational scaffolding supporting learner development.
Provision of working code examples and interactive demos enables hands-on learning and experimentation.
Comprehensive cross-platform testing documentation supports learners' understanding of real-world constraints.
Content teaches techniques for creating interactive visual and creative experiences. Discusses interactive diagrams, game design applications, and tools for creative expression. Supports readers' ability to participate in cultural and creative expression through interactive media.
FW Ratio: 60%
Observable Facts
Tutorial teaches creation of interactive visual experiences using draggable objects and real-time diagram response.
Content explicitly references applications like interactive diagrams, responsive design, and game design elements.
All examples and knowledge are freely available for readers to learn from and build upon.
Inferences
Teaching interactive design techniques enables readers to participate in creative expression through digital media.
Free access to technical knowledge about interactive media creation removes barriers to cultural participation in digital expression.
Content teaches techniques for creating interactive systems that enable user expression and communication. Advocates for tools and methods that support freedom of expression through interactive technology.
FW Ratio: 60%
Observable Facts
Tutorial teaches how to implement draggable objects and interactive user input systems across platforms.
Content is freely accessible without login, subscription, or restrictions.
Page includes working code examples and interactive demos readers can experiment with and adapt.
Inferences
Teaching implementation of interactive systems supports readers' ability to create systems for expression and communication.
Free distribution of technical knowledge about interaction design expands access to tools for expressive communication.
Content documents professional web development practices including testing, accessibility, and quality standards that support good working conditions and professional excellence.
FW Ratio: 50%
Observable Facts
Page documents comprehensive testing practices across multiple browsers and platforms.
Content emphasizes accessibility considerations and edge case handling as part of professional practice.
Inferences
Documenting best practices and thorough testing supports professional standards and worker dignity.
Free knowledge sharing about professional development enables developers to practice their craft more effectively.
All content freely accessible without barriers. Includes interactive code examples and live demos. Content is well-organized for self-directed learning. Multiple examples support different learning styles and use cases.
build 1ad9551+j7zs · deployed 2026-03-02 09:09 UTC · evaluated 2026-03-02 10:41:39 UTC
Support HN HRCB
Each evaluation uses real API credits. HN HRCB runs on donations — no ads, no paywalls.
If you find it useful, please consider helping keep it running.