That's source of the above list, gives a bit more explanation and rationale for each choice. I wrote it after failing to find any UI/UX development guidelines myself, and have had some positive responses from it.
But my advice: do not count on anything refreshing ever. Do not have animations, or transitions. Preload things to the correct spaces, then fill in. Reserve fixed white areas for output, and then fill in with black. (This works much better than the reverse!)
For quite long running processes, it might be worth having it behind a second page load.
Marsymars 21 minutes ago [-]
> But my advice: do not count on anything refreshing ever. Do not have animations, or transitions. Preload things to the correct spaces, then fill in. Reserve fixed white areas for output, and then fill in with black. (This works much better than the reverse!)
I would ammend to this that if you have an operation that can't complete quickly, it's better to have some kind of animation, even if it's notably slower than the already-slow screen refresh so that the user can tell that there's something happening.
That's a good shout, surprised this didn't occur to me actually as I was only recently dealing with a print-based feature at work. That's a helpful link, much appreciated.
a2ff6eeb0 2 hours ago [-]
I'd kill for regular UIs that did this.
Anoian 2 hours ago [-]
Out of curiosity: Why?
a2ff6eeb0 2 hours ago [-]
I'm constantly hitting the wrong button because some animation shifted it, some dialog popped over shit, or some notification decided now was a good time to cover the thing I was trying to do. The animations make things feel sluggish. And they don't even look good.
The only advice that doesn't improve all UIs is the bit about filling in with white and adding black after.
munk-a 36 minutes ago [-]
Moving elements on screens is viewed as slick but is also a strong statement that the website knows how it is best to consume itself and depriving the user of the control over how to interact with it. I think there's been a big misunderstanding from UX designers about what features users actually value over time and too great a reliance on short window A/B UX comparisons where the cool effect of animations has not yet worn through.
This is also complicated by the fact that _all UX changes are bad_ until the users get used to them. So if you genuinely believe you're providing a better UX you do need to ignore the initial wave of negativity - which makes it difficult to tell knee-jerk reactions from genuine criticism if you're glossing over the results.
37 minutes ago [-]
tmgldn 1 hours ago [-]
In general, we only really notice the animations that go wrong, like the ones that are too slow or get in the way, but when executed by the book (like the numbers material design published, or perhaps even slightly faster), they are a valuable way of communicating intuition for how to use the UI to a user.
But yes, I can understand the desire to remove them, or at minimum drastically reduce their visual impact for e-ink
a2ff6eeb0 45 minutes ago [-]
Did I stutter? I would like them all gone. I would like all visual elements to appear exactly in their final position and not resize, except in direct response to user interaction.
Animations are one of the things that I dread about using modern UIs. There are others too, no doubt.
Natalia724 4 hours ago [-]
[flagged]
ronakjain90 2 hours ago [-]
TRMNL recently open sourced a CSS framework made specifically for ePaper display [1], you can use the opensource firmware[2] and quickly hack together a good solution using off the self components.
I use eInk as my daily driver and do everything including ssh into tmux into vim and code on eInk.
I build all my UIs with Rust and Ratatui. It works very well.
So for web, I think it would translate to:
0) NO ANIMATION. You have like, 0.3 fps. You might poke somebody’s eye out if you animate something.
1) NO SCROLLING. Pagination only.
2) Use actual added visual element to tell the user where their input control / cursor / selection is. Don’t rely on color, font weight etc. Put an asterisk, Unicode arrow or something.
3) Direct(?) action controls. A button should DO something. Don’t give me a button that opens a drop-down that opens a menu to do something.
4) Use the whole screen! Keeping a pixel on/off costs nothing. Only changes do. So any whitespace is just sacrificing screen real estate and forcing more frequent changes.
Marsymars 19 minutes ago [-]
Asides from what's been mentioned, I'd suggest big touch targets. Because the UI is so slow, it's comparatively painful to miss a touch target, especially if the result of a miss is a UI refresh that then has to be undone before the original action can be retried.
storywatch 23 minutes ago [-]
A lot of times the e-ink display can handle much higher refresh rates but the driver board and software are trash.
See the modos for what a clean sheet eink design would look like.
Spitballing an idea. I don’t use e-ink stuff but had my hands on it back when personal readers were new to the market.
When scrolling arbitrary text on a computer via keyboard, I generally become mildly frustrated hitting Spacebar, PageUp, and/or PageDown because I lose continuity with what I’m reading. (Perhaps the momentary 60hz blur makes it worse?) I attribute that to having a hard time identifying where I last read.
If there’s a dedicated button to scrolling, consider having an adjustable distance. EX: 100% screen height down, 95% screen width down (keeps a sliver of the last page), or 80% screen width down (keeps a chunk of the last page).
If you’re working with a touch display, perhaps a hold-and-drag-to-scroll feature could work? Have no indicator when pressed and only refresh the screen when the user lifts their finger. (It’s certainly not ideal to provide no feedback that it’s being used. But it may be useful to a user aware of how it works. Would probably need a training demonstration in-device like what older operating systems had.)
Perhaps a dedicated scroll undo button? For when one makes a mis-input and wants it back where it was. I know I’ve seen my grandma get frustrated at that sort of thing on desktop/phone where it’s quick to fix.
ianbicking 3 hours ago [-]
I used Oberon [1] in school, and it had a bunch of funky UI ideas. One I liked it if you clicked on the scrollbar then it would scroll that position to the top. Like if you click the exact middle of the scrollbar then it would scroll up exactly half a page. Right click went the opposite direction, and middle click went to an absolute position. When reading that means you can choose a paragraph as the break point, click next to it, and that paragraph is now at the top of the page. It's nice too when you reach a heading that might be only a little way down the page, but you can reliably scroll so it's at the top of the page and feel like you are really "starting" a new section.
It could work well with e-ink because it's basically not interactive; you don't press and hold and move around to adjust it just right, instead you can confidently get an exact scroll position with one tap.
NeXT also had "click to scroll to here" and Mac OS X added it as an option (which I always enable).
layer8 2 hours ago [-]
You could have a scroll-down bar on one side and a scroll-up bar on the other side that work that way (given the lack of left/right click options). That could be pretty intuitive for touch.
BoxOfRain 42 minutes ago [-]
> If you’re working with a touch display, perhaps a hold-and-drag-to-scroll feature could work? Have no indicator when pressed and only refresh the screen when the user lifts their finger.
I like this idea, definitely going to test it out.
seemack 4 hours ago [-]
Riffing of the predictability of an adjustable distance, a similar option might be something like a marker/inverse colour applied to a line immediately before the scroll. The marker would help orientation after the scroll and disappear after some time. You could also scan the text of course but a marker would be much faster.
skydhash 4 hours ago [-]
There’s a lot of patterns in TUI due to slow terminals that can be reused, especially with full/half page scrolling instead of line by line. And like less and emacs (vim may have that too), the full screen scroll may be minus a configurable amount of line.
There can be also a configurable wait time for refreshing the screen (like top) instead of assuming 60hz
longnguyen 5 hours ago [-]
Not a web UI, but I recently built this[0] for my Boox Note Air 5c tablet. You may find it useful (check the source code in GitHub)
Hello Tom Riddle! I wanted to do something like this with my pinenote a while ago, but then lost interest. My idea was to write down equations and draw diagrams of maths problems and have it solve things for me.
blululu 2 hours ago [-]
If you are just trying to get something quick and simple that works for rendering react components then this repo is solid:
The Bigme is a pretty high quality eink display, but some core properties of eink are present here so I’ll offer some generic guidance:
The contrast on any epaper display is lower than on a modern lcd/oled display. So you want to avoid or modify ui systems that try to diminish contrast for stylistic reasons. Or more explicitly: maximize the contrast wherever legibility matters.
It is pretty common in pretty much every modern design system for web/mobile to diminish contrast so you may just need to go in and tweak things yourself.
Second: you cannot rely on color as an accent. A lot of UI does this (submit buttons). Defining a consistent accent pattern is necessary to replace patterns that rely on color. This can be outlines/borders or inversions.
Stylistically I think that sharper content looks better on eink than smoother content, but on a 300 dpi display with 16 grays this is less of a hard point.
BoxOfRain 44 minutes ago [-]
Cheers, will check that repo out.
>you cannot rely on color as an accent
Yeah definitely found this using some 'normal' webapps, default dark mode makes it worse too.
pshc 2 hours ago [-]
I've been experimenting with a Remarkable Paper Pro.
Additive inking works pretty well. As long as I am adding black ink to the page, everything is responsive. But if I want to push other updates asynchronously, or (god forbid) erase something, then there is the danger of clobbering the user's inkwork. I don't have any deep insights here, beyond waiting for human confirmation before updating anything.
I wonder if it is possible to detect when the pen tip has "left" the proximity of the page? If I knew this, I'd know when it was safe to update the UI.
I have decided not to use any scrolling at all. I am paginating everything and positioning input areas near the vertical middle of the device for comfort.
tracker1 4 hours ago [-]
Beyond the early MacOS UI/UX, I would look at pre-smartphone devices as well. Palm pilot, phones, etc. These will give you ideas on what you can accomplish with lower resolution UI screens.
For that matter, look at good TUI applications... (not the render path, but the result as a whole) ... blocking/spacing and clear controls, etc... though you'll be more touch, less tab/enter, etc. A TUI App renders by characters, but you can still learn from the overall layouts. Simplified menu lists with fewer items in a hierarchy, etc.
If you can and have physical buttons, think long and hard about their functions, especially if you have a limited number... these should do your most important things.
dredmorbius 38 minutes ago [-]
Early monochrome devices were largely liquid crystal rather than electrophoretic displays.
The key differences:
- LCD was far lower resolution, often element-based rather than pixel-based displays (though the latter also existed).
- Screen response is far faster, with no ghosting. Electropheretic refreshes tend to take time, particularly at higher-quality or colour settings. Modern LCDs are used in high-performance displays at 60--120 Hz refresh or greater. By comparison, a fast EP/e-ink display might reach a few herz, and many require a substantial fraction, if not more than a second, to fully paint.
- Contrast was low. LCD relies on polarisation filters which greatly reduces foreground/background distinction.
That's not to say that some lessons cannot be learned, but the media do differ.
hirako2000 38 minutes ago [-]
Rmkit is a great foundation.
Not browser based. Not sure why it would matter.
1 hours ago [-]
2 hours ago [-]
ramses0 4 hours ago [-]
Look back to the olden days of "drag proxies" or outline/wireframe drags.
Specifically for your example of maps, I would stick mostly to border buttons or UDLR gestures to "page" around rather than than trying to show a smoothly refreshing drag area.
Since you can _see_ the screen refreshing/drawing, and assuming you're using +/- buttons instead of "pinch to zoom", try drawing animations "in layers", ie: spend 100-300ms slapping "zoom tunnel boxes" trying to keep the user oriented while interacting.
Break the tiles into "thick => thin => letters => detail" and wait for them to quiesce before stacking more layers.
Rule #1 of optimization, you can never do something faster, you can only "do less"!
Rule #2 of optimization (amdahls law?)- optimize the slowest parts before you optimize the faster parts (and there's a limit to each!).
...from an end-user perspective, I would love if you respected my time and concentration when doing something "interactive".
Imagine scrolling through an e-ink address book. Imagine you spam only the (properly placed / laid out) first letter of first name and first letter of last name, and then after ~500ms of no movement, only THEN write out the remaining letters, phone numbers, icons, etc.
For e-ink interactive: every (slow, expensive) pixel that you write that gets scrolled off the screen is wasted time.
#1 - do less. In interactive cases, draw less pixels.
#2 - keep context. Use proxies/wireframes, "first letters", and "bold strokes".
#3 - fill in layers. Think "iterative Mona Lisa" and "progressive jpeg"
#4 - actually being slower is okay if you end up "feeling faster" or "seeming cooler"
Lean in to the defects and quirks of e-ink, make them feel intentional rather than trying to duplicate 60fps 16M colors.
jeffnash 2 days ago [-]
If you find out, let me know! I have been working on a port of Excalidraw to my ReMarkable Pro and it's opened my eyes to how much we take for granted in terms of UI interactions when we assume we have a 30+hz screen with no to minimal ghosting. Layout has to become as monotonic as possible, especially in the absence of user interaction; in other words, the app itself should never be doing anything to disrupt prior renders on its own unless you are clearing the whole page. When it is the user interacting with the app, try to minimize the number of intermediate states their interactions produce. This does NOT mean "just don't render those intermediate states", however. It means make the interactions themselves not require intermediate feedback. Essentially, users (and developers) are so accustomed to just being able to manipulate things with no side effects that you have to make every possible interaction as intentional as possible, leaving them less frustrated when they do see a refresh. If an operation is going to cause a refresh, it should happen at a moment the user already understands as a semantic boundary.
You obviously cannot always avoid this, so when there is a hard requirement for continuous feedback, use a degraded/cheap transient representation and refresh at commit of that action. With E-Ink, every interaction has to be a trade off between ghosting and latency. One common pattern I ended up coming back to is intermediate states that optimize for latency at the expense of ghosting until the UI action is 'complete'. This will allow refreshes to be associated with the satisfaction of finality. Funny enough, many of them harken back to old UI paradigms from when computer graphics weren't as powerful as today. When resizing, I simply draw the outline of the shape with a fast update mode that tolerates more ghosting until the user releases their finger or stylus, at which point, after a small lag to account for an 'oops, just a tiny bit bigger/smaller', I do a localized refresh of the union of the old + new area.
The outline itself is deliberately faint, so the refresh doesn't have to be as intense if its a small delta.
For your streaming LLM case, I would recommend doing it in chunks, like you said, perhaps doing a hard refresh of everything but the input box + moving the tail of last sent message + beginning of stream to the top upon send. Of course, this implies that you know exactly how long the response will be, which you don't. The challenge will be coming up with a UI paradigm that doesn't make it look awkward if the response only goes down to the middle of the screen while also nicely doing a clean refresh pre-emptively if it's clear it will overflow and need to move to the top. For the former case, find some way where it looks not completely awkward if it's partial and then do refresh of the chat history area and move it to its proper fitted position once the user clicks on the input box again to type their next reply. For the latter, perhaps embedding some kind of remaining space meter at the bottom that indicates when you'd need to do this move + refresh action (don't directly label it that way) would enable the user to anticipate it more and be less frustrated when it happens, as they are psychologically awaiting the next part of the answer/reasoning, not surprised at a sudden flash.
For the actual streaming, you'll have to ensure that your text alignment works in such a way that once a line is rendered to the screen, its placement is final (in other words, no streaming intra-word, as you need to know if the word will fit in remaining space on the line before rendering it). Basically, avoid retroactive reflow like a PDF document with a fixed layout, not like this text box I am typing in with a resize handle at the lower right. You'll also probably want to disable scrolling during generation as well, or at least make some sort of discrete pagination mechanism for going back and forth.
In any event, I look forward to seeing whatever it is you're building!
BoxOfRain 54 minutes ago [-]
This is really helpful, much appreciated.
DANmode 3 hours ago [-]
Just buy a Palm Pilot, and/or review old PalmOS screenshots.
Not kidding.
BoxOfRain 49 minutes ago [-]
I just had a look, definitely a lot to draw from there!
1. Persistence is free.
2. Pixels are cheap
3. Paints are expensive
4. Refreshes are slow
5. Colour is limited to nonexistent.
6. Pagination over scroll.
7. Full refresh (of page or portion) over pan.
8. Reflective rather than emissive.
9. Minimise animation.
10. Line-art or halftones over shade gradients (images).
<https://news.ycombinator.com/item?id=31396797>
I've reiterated (and occasionally revised) that a few times, see:
"E-Ink Design Principles for Web and Applications" <https://diaspora.glasswings.com/posts/638a8d10e041013afba844...>
That's source of the above list, gives a bit more explanation and rationale for each choice. I wrote it after failing to find any UI/UX development guidelines myself, and have had some positive responses from it.
And on HN, search for "pixels are cheap" by me: <https://hn.algolia.com/?dateRange=all&page=0&prefix=false&qu...> (about 15 results, this comment included).
https://www.mediapoint.com.au/design-tips/composition-layout... might be moderately helpful.
But my advice: do not count on anything refreshing ever. Do not have animations, or transitions. Preload things to the correct spaces, then fill in. Reserve fixed white areas for output, and then fill in with black. (This works much better than the reverse!)
For quite long running processes, it might be worth having it behind a second page load.
I would ammend to this that if you have an operation that can't complete quickly, it's better to have some kind of animation, even if it's notably slower than the already-slow screen refresh so that the user can tell that there's something happening.
The only advice that doesn't improve all UIs is the bit about filling in with white and adding black after.
This is also complicated by the fact that _all UX changes are bad_ until the users get used to them. So if you genuinely believe you're providing a better UX you do need to ignore the initial wave of negativity - which makes it difficult to tell knee-jerk reactions from genuine criticism if you're glossing over the results.
But yes, I can understand the desire to remove them, or at minimum drastically reduce their visual impact for e-ink
Animations are one of the things that I dread about using modern UIs. There are others too, no doubt.
Disclosure: I work at TRMNL.
[1] https://github.com/usetrmnl/trmnl-framework
[2] https://github.com/usetrmnl/trmnl-firmware
I build all my UIs with Rust and Ratatui. It works very well.
So for web, I think it would translate to:
0) NO ANIMATION. You have like, 0.3 fps. You might poke somebody’s eye out if you animate something.
1) NO SCROLLING. Pagination only.
2) Use actual added visual element to tell the user where their input control / cursor / selection is. Don’t rely on color, font weight etc. Put an asterisk, Unicode arrow or something.
3) Direct(?) action controls. A button should DO something. Don’t give me a button that opens a drop-down that opens a menu to do something.
4) Use the whole screen! Keeping a pixel on/off costs nothing. Only changes do. So any whitespace is just sacrificing screen real estate and forcing more frequent changes.
See the modos for what a clean sheet eink design would look like.
https://www.crowdsupply.com/modos-tech/modos-flow#products
When scrolling arbitrary text on a computer via keyboard, I generally become mildly frustrated hitting Spacebar, PageUp, and/or PageDown because I lose continuity with what I’m reading. (Perhaps the momentary 60hz blur makes it worse?) I attribute that to having a hard time identifying where I last read.
If there’s a dedicated button to scrolling, consider having an adjustable distance. EX: 100% screen height down, 95% screen width down (keeps a sliver of the last page), or 80% screen width down (keeps a chunk of the last page).
If you’re working with a touch display, perhaps a hold-and-drag-to-scroll feature could work? Have no indicator when pressed and only refresh the screen when the user lifts their finger. (It’s certainly not ideal to provide no feedback that it’s being used. But it may be useful to a user aware of how it works. Would probably need a training demonstration in-device like what older operating systems had.)
Perhaps a dedicated scroll undo button? For when one makes a mis-input and wants it back where it was. I know I’ve seen my grandma get frustrated at that sort of thing on desktop/phone where it’s quick to fix.
It could work well with e-ink because it's basically not interactive; you don't press and hold and move around to adjust it just right, instead you can confidently get an exact scroll position with one tap.
[1] https://en.wikipedia.org/wiki/Oberon_(operating_system)
I like this idea, definitely going to test it out.
There can be also a configurable wait time for refreshing the screen (like top) instead of assuming 60hz
[0]: https://inka.page
https://github.com/marcomattes/epaper-components/tree/main
The Bigme is a pretty high quality eink display, but some core properties of eink are present here so I’ll offer some generic guidance:
The contrast on any epaper display is lower than on a modern lcd/oled display. So you want to avoid or modify ui systems that try to diminish contrast for stylistic reasons. Or more explicitly: maximize the contrast wherever legibility matters. It is pretty common in pretty much every modern design system for web/mobile to diminish contrast so you may just need to go in and tweak things yourself.
Second: you cannot rely on color as an accent. A lot of UI does this (submit buttons). Defining a consistent accent pattern is necessary to replace patterns that rely on color. This can be outlines/borders or inversions.
Stylistically I think that sharper content looks better on eink than smoother content, but on a 300 dpi display with 16 grays this is less of a hard point.
>you cannot rely on color as an accent
Yeah definitely found this using some 'normal' webapps, default dark mode makes it worse too.
Additive inking works pretty well. As long as I am adding black ink to the page, everything is responsive. But if I want to push other updates asynchronously, or (god forbid) erase something, then there is the danger of clobbering the user's inkwork. I don't have any deep insights here, beyond waiting for human confirmation before updating anything.
I wonder if it is possible to detect when the pen tip has "left" the proximity of the page? If I knew this, I'd know when it was safe to update the UI.
I have decided not to use any scrolling at all. I am paginating everything and positioning input areas near the vertical middle of the device for comfort.
For that matter, look at good TUI applications... (not the render path, but the result as a whole) ... blocking/spacing and clear controls, etc... though you'll be more touch, less tab/enter, etc. A TUI App renders by characters, but you can still learn from the overall layouts. Simplified menu lists with fewer items in a hierarchy, etc.
If you can and have physical buttons, think long and hard about their functions, especially if you have a limited number... these should do your most important things.
The key differences:
- LCD was far lower resolution, often element-based rather than pixel-based displays (though the latter also existed).
- Screen response is far faster, with no ghosting. Electropheretic refreshes tend to take time, particularly at higher-quality or colour settings. Modern LCDs are used in high-performance displays at 60--120 Hz refresh or greater. By comparison, a fast EP/e-ink display might reach a few herz, and many require a substantial fraction, if not more than a second, to fully paint.
- Contrast was low. LCD relies on polarisation filters which greatly reduces foreground/background distinction.
That's not to say that some lessons cannot be learned, but the media do differ.
Not browser based. Not sure why it would matter.
Specifically for your example of maps, I would stick mostly to border buttons or UDLR gestures to "page" around rather than than trying to show a smoothly refreshing drag area.
Since you can _see_ the screen refreshing/drawing, and assuming you're using +/- buttons instead of "pinch to zoom", try drawing animations "in layers", ie: spend 100-300ms slapping "zoom tunnel boxes" trying to keep the user oriented while interacting.
Break the tiles into "thick => thin => letters => detail" and wait for them to quiesce before stacking more layers.
Rule #1 of optimization, you can never do something faster, you can only "do less"!
Rule #2 of optimization (amdahls law?)- optimize the slowest parts before you optimize the faster parts (and there's a limit to each!).
...from an end-user perspective, I would love if you respected my time and concentration when doing something "interactive".
Imagine scrolling through an e-ink address book. Imagine you spam only the (properly placed / laid out) first letter of first name and first letter of last name, and then after ~500ms of no movement, only THEN write out the remaining letters, phone numbers, icons, etc.
For e-ink interactive: every (slow, expensive) pixel that you write that gets scrolled off the screen is wasted time.
#1 - do less. In interactive cases, draw less pixels.
#2 - keep context. Use proxies/wireframes, "first letters", and "bold strokes".
#3 - fill in layers. Think "iterative Mona Lisa" and "progressive jpeg"
#4 - actually being slower is okay if you end up "feeling faster" or "seeming cooler"
Lean in to the defects and quirks of e-ink, make them feel intentional rather than trying to duplicate 60fps 16M colors.
You obviously cannot always avoid this, so when there is a hard requirement for continuous feedback, use a degraded/cheap transient representation and refresh at commit of that action. With E-Ink, every interaction has to be a trade off between ghosting and latency. One common pattern I ended up coming back to is intermediate states that optimize for latency at the expense of ghosting until the UI action is 'complete'. This will allow refreshes to be associated with the satisfaction of finality. Funny enough, many of them harken back to old UI paradigms from when computer graphics weren't as powerful as today. When resizing, I simply draw the outline of the shape with a fast update mode that tolerates more ghosting until the user releases their finger or stylus, at which point, after a small lag to account for an 'oops, just a tiny bit bigger/smaller', I do a localized refresh of the union of the old + new area.
The outline itself is deliberately faint, so the refresh doesn't have to be as intense if its a small delta.
For your streaming LLM case, I would recommend doing it in chunks, like you said, perhaps doing a hard refresh of everything but the input box + moving the tail of last sent message + beginning of stream to the top upon send. Of course, this implies that you know exactly how long the response will be, which you don't. The challenge will be coming up with a UI paradigm that doesn't make it look awkward if the response only goes down to the middle of the screen while also nicely doing a clean refresh pre-emptively if it's clear it will overflow and need to move to the top. For the former case, find some way where it looks not completely awkward if it's partial and then do refresh of the chat history area and move it to its proper fitted position once the user clicks on the input box again to type their next reply. For the latter, perhaps embedding some kind of remaining space meter at the bottom that indicates when you'd need to do this move + refresh action (don't directly label it that way) would enable the user to anticipate it more and be less frustrated when it happens, as they are psychologically awaiting the next part of the answer/reasoning, not surprised at a sudden flash.
For the actual streaming, you'll have to ensure that your text alignment works in such a way that once a line is rendered to the screen, its placement is final (in other words, no streaming intra-word, as you need to know if the word will fit in remaining space on the line before rendering it). Basically, avoid retroactive reflow like a PDF document with a fixed layout, not like this text box I am typing in with a resize handle at the lower right. You'll also probably want to disable scrolling during generation as well, or at least make some sort of discrete pagination mechanism for going back and forth.
In any event, I look forward to seeing whatever it is you're building!
Not kidding.