Skip to main content
Questions people actually ask before and after buying Inkwell. Each answer links to the page that covers it properly.

Before you buy

Unreal Engine 5.5, 5.6, 5.7 and 5.8. Fab lists a separate download per engine version; pick the one matching your project and it installs prebuilt for that engine. Every release is compiled against all four before it goes up.Nothing in the plugin uses experimental or preview engine APIs — the runtime module depends only on Core, CoreUObject, Engine, InputCore, EnhancedInput, Slate, SlateCore, UMG, DeveloperSettings, plus private RenderCore and RHI. The plugin declares a dependency on the Enhanced Input plugin, which is enabled by default in every UE5 project.One difference worth knowing about, because Unreal gives no warning when it bites: engine packages load forward only, so content saved by a newer editor cannot be opened by an older one at all. The 5.5 and 5.6 downloads therefore carry content authored on 5.5. It is the same documents, meshes and materials, with one exception — the example Input Actions in those two builds ship unbound, so wire your own keys rather than relying on IMC_InkwellDefault. Nothing depends on them: the demo pawn builds its own input at runtime.See Installation.
You never write C++. Every component, function, property, event and struct is Blueprint-exposed — the whole surface is on the Blueprint API reference, and the demo content is wired in Blueprint only.The plugin contains C++, which matters in exactly one place: how you install it.
  • Installed to the engine from Fab — it arrives with prebuilt binaries for that engine version, and a Blueprint-only project can enable and use it with no toolchain at all.
  • Vendored into your project’s Plugins/ folder as source — the plugin then has to be compiled, which makes yours a code project and requires a C++ toolchain. This is the right choice for teams that keep plugins in source control.
Either way, you author pages in a window and wire them with Blueprint nodes.
Yes. Copies obtained through Fab are governed by the Fab Content Licensing Agreement, which grants the purchaser the right to use, modify and distribute the plugin as part of their own projects and products, commercial ones included. What you may not do is redistribute Inkwell itself as a plugin or asset.The five bundled font families — Courier Prime, Crimson Text, Kalam, Patrick Hand and Great Vibes — are all SIL Open Font License 1.1, which permits bundling and redistribution in commercial products. The full license texts ship inside the plugin at Plugins/Inkwell/Resources/Licenses/, and the notices are in Plugins/Inkwell/THIRD_PARTY_NOTICES.md. Keep those files in your distribution and the OFL’s conditions are met.If you import your own licensed fonts with Import Font Family, their licenses are your responsibility, not Inkwell’s.
Yes — that is the intended path, and the quickstart is built around it.The production pieces make no assumptions about a trace, a key, a channel, or a pawn class. Put an Ink Page Reader (UInkPageReaderComponent) on your player and call it from whatever your interact verb already is:
  • Begin Reading Actor (Actor) → floats that actor’s page into view.
  • End Reading → puts it back, descending from wherever it is to exactly where it lay.
  • Next Page / Previous Page → turn the page being read, in book order — a sheet with an authored back reads front, then back, then the next page.
  • Flip Page → turns the held sheet over on its own.
The bundled Ink Page Interactor, the demo character and game mode, the exhibit component and the prompt widget are demo/reference only — they exist so the showroom plays out of the box and so you can read a complete working example of those calls. They are not recommended for production use. The reader is production: it is the presentation engine your own interaction system drives.The Ink Page actor also exposes Notify Interacted and Notify Focus Changed so your base interactable class can drive its On Interacted and On Focus Changed events without Inkwell touching input at all.Full walkthrough: Integration.

The first hour

The traps that cost new buyers the most time, in the order they are usually hit.
Inkwell is not enabled by default. Installing from Fab puts the plugin in the engine; each project still has to turn it on:
  1. Edit → Plugins, search Inkwell, tick Enabled.
  2. Restart the editor when prompted.
Until then there is no Inkwell section in Project Settings, no Page Document in the right-click menu, and no toolbar button — nothing is broken, the plugin is just off. See Installation.
The Content Browser hides plugin content by default. Click Settings (top-right of the Content Browser) and tick Show Plugin Content — the /Inkwell/ folder appears, with the showroom map at /Inkwell/Maps/L_InkwellShowroom, the bundled documents under /Inkwell/DemoProps/Documents/ (the PD_ assets) and the example input assets under /Inkwell/Examples/Input/.Open the showroom and press Play: it brings its own game mode and pawn, so it works in an empty project with no setup. WASD and mouse to walk, E to read, Left/Right or the mouse wheel to turn (and flip) pages, F to flip a sheet over, Q to put it back.
If your interactor (or your own trace) runs on a custom trace channel, two Inkwell surfaces need to be told:
  • Ink Page Stack sheets answer only their own Sheet Trace Channel (Visibility by default) and ignore every other channel. Set it to your channel on every stack, or the pile never focuses — with no warning anywhere. The interactor’s Trace Channel and the stack’s Sheet Trace Channel must agree.
  • The sheet spawned by Procedural Paper on an Ink Page Display blocks only the Visibility channel, deliberately, so it cannot intercept AI sight or hitscan traces. If you trace a custom channel at it, add that response yourself: Get Procedural Paper MeshSet Collision Response To Channel.
Also check Can Be Picked Up on the display component — off, the page is deliberately scenery and the bundled interactor’s trace treats it as nothing.
That mode lays the ink onto whatever material the mesh already wears — and that material must contain the MF_InkwellPageInk material function, or the ink silently does not appear. No error is raised: the silence is the mode’s contract, because Inkwell writes parameters the material simply does not have.Open the mesh’s material, drop MF_InkwellPageInk into its graph, feed it your paper color and wire its InkedColor output into your final color. Every Inkwell control — ink, tints, strength, face mapping — starts working immediately. See Rendering → the MF_InkwellPageInk contract.
The Ink Page Reader holds pages at a computed distance by default — Fit Page To View works the distance out from the page’s real size and the camera’s field of view, so a poster and a sticky note both fill the same slice of the screen. The knobs, all on the reader:
  • View Fill Fraction (0.75) — how much of the screen the page covers. Higher is closer.
  • Min View Distance (18 cm) / Max View Distance (90 cm) — the fitted distance’s clamps. The max is what keeps a big page from being pushed through the furniture behind it.
  • Keep Clear Of Obstructions (on) — a trace pulls the held page in front of whatever the player is facing, so reading off a podium does not sink the page into it.
  • Turn Fit Page To View off and the flat View Distance (45 cm) is used verbatim — the right choice for a project that wants one tuned framing to the centimeter.

Using it in a project

Yes. Documents are mutable at runtime through Blueprint:
  • Set Page Markup (Page Index, Markup) — rewrites the page’s first text box.
  • Set Layer Markup (Page Index, Layer Index, Markup, Side) — rewrites one specific text layer, on either side of the sheet.
  • Add Page, Remove Page, Set Page — restructure the document.
  • Set Page Layers, Add Page Layer, Remove Page Layer, Move Page Layer — edit the stack.
Every one of those invalidates the bake cache for you. Then call Refresh on the display component so it re-bakes and re-applies. If you changed something none of those cover, call Bump Revision on the document first.Two things to know. Get Page returns a copy — push it back with Set Page. And mutating a document asset changes it for every actor in the world showing it, so build a runtime copy (Duplicate Object into the transient package, or a fresh UInkPageDocument) when you want one note to differ from the others.Full recipes: Runtime Documents.
There is no hard limit on either. Pages is a plain array and documents are ordinary Data Assets, so the real limits are memory and taste.What actually costs something:
  • Baked pages. Pages are baked lazily, one side at a time, the first time something asks for one. An unread page costs only the bytes of its authored text, and an unauthored back never costs anything at all. A page that has been read holds an ink render target in the world’s cache — about 5.5 MB at the default 1024 × 1400. A 20-page journal read cover to cover is therefore around 110 MB of cache until the world ends. That number, not the page count, is what to plan against; see page size and what it costs.
  • Sheet meshes on a stack. Ink Page Stack never creates more than Max Visible Sheets meshes (16 by default). A 200-page book shows a sliding window of sheets and re-bakes the hidden bottom one as you read, so the mesh count is constant.
  • Layers and strokes. Each visible layer is one slot in the page’s widget tree, and each stroke is a polyline plus round dabs at its joins. Thousands of strokes on one page make its bake slower; they cost nothing afterwards.
Unloaded documents cost nothing at all. A game with a thousand notes scattered across a world is fine as long as the player does not read all thousand in one session.
Nothing in Inkwell is replicated. No property is marked Replicated, and no RPCs exist. Page state is local, which is the right default — a baked page is client-side presentation, and replicating render targets would be absurd.For a shared world, replicate your own state and drive Inkwell from it on each machine:
  • Replicate the page index (or the document reference) on your own actor and call Set Page Index / Set Document on the Ink Page Display from a RepNotify. Every client bakes locally and they stay in sync.
  • Do not let two players read the same placed page actor. Ink Page Reader physically moves the page actor’s transform, disables its collision and stops its physics, all locally. On a replicated actor the server will fight it, and everyone else watches the note fly across the room. Give each player a client-only page to read, or read from a UMG page widget instead, which touches no world state at all.
  • The reader’s movement and look locks call SetIgnoreMoveInput / SetIgnoreLookInput on the local controller, so they behave correctly for the reading player only — which is what you want.
Yes, with one thing to wire yourself.The Ink Page Reader places the page from the player’s view point — for a possessed pawn it reads through the controller’s camera manager, which in VR is the tracked HMD view. So Begin Reading floats the page in front of the headset and, with Follow Camera on, keeps it there as the player looks around. Fit Page To View computes the hold distance from the rendered field of view, so it behaves sensibly in a headset; tune View Fill Fraction and Min View Distance to taste, and turn Lock Look While Reading off — freezing the view is the wrong behavior in VR, and can be uncomfortable.The part to wire yourself is picking the page. The bundled Ink Page Interactor traces from the view point, which makes it gaze-based — and it is a demo component anyway. For motion controllers, call Begin Reading Actor from your own controller trace or grab event.Pages in the world are ordinary lit meshes, so they work in VR with no special handling. A full-screen UMG page does not — put the Ink Page widget in a world-space Widget Component instead.
Page text is a String of rich-text markup on the asset, not an FText, so it does not flow through Unreal’s localization pipeline automatically. Two approaches work:
  1. One document per language. Author PD_Letter_EN, PD_Letter_FR, and pick between them with Set Document using your own culture check. This keeps the authored styling, the layers and the layout intact per language, which matters when a translation is twice as long as the original.
  2. Drive the text at runtime. Keep the text in a String Table or an FText you format, and push it with Set Page Markup. This is the lighter option, and the right one for generated text — a player’s name, a quest log, a date.
Rich-text markup treats < as the start of a tag. A localized string containing a literal < will not parse. Strip or escape angle brackets in text you inject with Set Page Markup, and remember that a runtime string carries no styling of its own — it renders in the document’s Default Text Style unless you wrap it in markup yourself.
Right-to-left scripts are not something Inkwell handles itself: pages go through Slate’s rich text block with the engine’s default shaping, and Inkwell adds no bidirectional handling of its own. Test a page in your target script before committing to it.

Packaging and performance

Yes, and the editor-only half removes itself: InkwellEditor is an Editor module, so the Page Editor window, the asset factory, the menus and the font tools never enter a packaged build. Only the Inkwell runtime module ships.Three things are worth checking before you package:
  1. Save documents from the Page Editor’s Save Document button — see the fonts entry below.
  2. Assets you repointed in settings. If you pointed Page Ink Material, Page Sheet Mesh or Default Paper Texture at a project asset that nothing else in the project references, make sure it is included in the cook — Additional Asset Directories to Cook is the usual route.
  3. Audit the demo key bindings. The demo/reference Ink Page Interactor should not ship on a production pawn at all — but if it does, turn off its Enable Default Input, or E, Q, the arrow keys and the mouse wheel stay live in your released game. The reader’s own Bind Flip Key (F by default) is a real runtime binding too; turn it off once your own input drives Flip Page.
More detail: Installation → before you package.
They will, provided you save documents the right way.Rich-text markup stores fonts as string paths, which the cooker cannot see. A page whose fonts were only named in markup would cook away and render fallback glyphs in the packaged game — a bug that never shows up in the editor. Inkwell handles this with the document’s Font References array of hard references.That array is rebuilt by Save Document in the Page Editor, and by nothing else. Saving the asset with Ctrl+S from the Content Browser, or letting it be saved as part of a level save, does not refresh it.
After changing fonts on a page, click Save Document. It rebuilds the references from every text layer on every page — plus the document’s effective default font, so a project-wide settings font gets a hard reference too — and then saves the asset.
Never delete or hand-edit Font References. See Fonts.
Almost nothing in steady state, with one spike you can control.The spike is the bake: composing a page into a Slate widget tree and rendering it into a render target, on the game thread, twice (the second draw is what makes wrapped and justified text land correctly). It happens once per page side, per document, per content revision, per world — the result is cached and shared by everything showing that page. A page of text is cheap; a page carrying thousands of drawn strokes is the expensive case.If a first page turn hitches, pre-warm: call Get Page Ink Texture for the pages you know will be read, during a load screen or on Begin Play.Steady state is a mesh with one dynamic material instance. Ticking is minimal:Memory is the thing to actually budget: see page size and what it costs, and resist raising Page Size Pixels above what the page occupies on screen — the ink target has no mip chain, so oversizing costs image quality as well as bytes.

Something looks wrong

First, filter the Output Log by LogInkwell — every misconfiguration Inkwell can detect logs a warning there naming the fix. Then work down this list — it is ordered by how often each one is the answer.
  1. No document is assigned. A display with no Document deliberately shows blank paper rather than stale ink. Check the Document pin.
  2. It is editor startup. A component can register before the Slate renderer is usable, so the first preview bake fails and the page pops in a moment later. Inkwell retries automatically. See editor preview.
  3. There is no mesh to draw onto. Procedural Paper only spawns a sheet when the actor has no mesh of its own and has a root component. With Procedural Paper off and no mesh, nothing draws — LogInkwell warns: “has no mesh to draw onto — set Target Mesh, add a mesh component to the actor, or enable Procedural Paper.”
  4. Paper Mode is Mesh’s Own Material and the material lacks MF_InkwellPageInk. No ink, no error — by contract. See the first hour above.
  5. Apply On Begin Play is off. Then nothing draws until you call Refresh yourself.
  6. The text layer is hidden, or its opacity is 0. Hidden layers are skipped by the bake as well as the preview. Check the eye toggle in the Page Editor’s Layers panel.
  7. Ink Strength is 0 on the document. That is invisible ink, working as designed.
  8. The page has no Text layer. Only reachable by deleting the Text layer from script with Remove Page Layer — a front stack handed to Set Page Layers gets a Text layer back automatically, and the Page Editor refuses to delete a page’s last one. Find Text Layer Index returning -1 confirms it.
  9. The page ink material could not be loaded. Then displays fall back to the mesh’s existing material and the ink parameters do nothing on it. The Output Log carries one LogInkwell warning naming the setting to fix. See Project Settings.
It is not the document. A Page Document has exactly one physical dimension — Page Width Cm — and derives the height from the Page Size Pixels aspect ratio, so the paper is always the same shape as the page you authored and the writing cannot come out stretched. Page Scale on the display component scales both axes together for the same reason.So the distortion is in the mesh. In order of likelihood:
  1. Non-uniform scale on the page actor or on the mesh component. The baked ink is mapped across the mesh’s page-face UVs, so a sheet scaled 2 × 1 stretches every letter on it. Reset the scale and let Inkwell size the paper.
  2. You supplied the mesh. Inkwell never resizes a mesh you provided — an explicit Target Mesh, or any mesh component already on the actor, is drawn onto exactly as built. Size it yourself to the page’s aspect ratio, or delete it and let Procedural Paper spawn the sheet.
  3. Auto Size Sheet is off on the Ink Page actor. That actor owns its sheet and fits it to the page only while that toggle is on.
  4. The mesh’s UVs are not clean 0–1 across the page face. For a mesh that was not unwrapped for Inkwell — a face parked in the corner of an atlas, an unwrap that runs sideways — set the display component’s Page Face mapping (Auto Fit measures the region from the mesh; quarter turns fix a sideways unwrap) instead of re-unwrapping. The bundled SM_PageSheet needs none of that.
There are two different features called “paper”, and the fix depends on which one you mean.“There is no sheet in the level at all.” That is Procedural Paper on the Ink Page Display component — the toggle that spawns a mesh. It stays out of your way on purpose:
  • It only spawns when the actor has no mesh component of its own. Any mesh already on the actor, or an explicit Target Mesh, is used instead and no sheet appears.
  • It needs the actor to have a root component to attach to.
  • It is grayed out on an Ink Page Stack, which always spawns one sheet per page instead.
“The sheet is there but the paper isn’t.” That is the document’s Paper Mode, which decides where the paper comes from:
  • Texture asset (Texture) with Paper Texture empty resolves no paper at all. Inkwell flips UsePaperTexture to 0 and the material falls back to its flat paper color, which reads as blank, evenly lit stock. Assign a texture, or switch to Generated.
  • Generated (Procedural) draws the sheet from the ruling settings and needs no assets whatsoever — the fastest way to lined, grid or dotted stock. See Paper.
  • Custom Material and Mesh’s Own Material bring their own paper — Inkwell deliberately writes no paper parameters in those modes. If the surface looks wrong there, the material graph owns it.
  • A page’s Paper Override beats the document. If one page in a document is wrong, check whether it carries an override — and a back has its own Back Paper and Back Paper Override to check.
  • On the Ink Page widget, Show Paper must be on.
  • On a custom page material set project-wide, the paper only appears if the material carries MF_InkwellPageInk or parameters matching the names under Material Parameters.
The ink is baked in linear space — the render target is created with SRGB off and gamma 1.0, and the widget renderer runs without gamma correction. Sample the ink texture with a LinearColor sampler. A default sRGB sampler washes every color out by exactly one gamma curve. Building on MF_InkwellPageInk avoids the problem entirely — it samples the ink correctly for you. See the ink render target.
In the editor, an edit to the asset re-bakes and re-sizes every placed page showing it, immediately. If that is not happening, the component’s Preview In Editor is off.At runtime, mutate the document through its Blueprint API — Set Page Markup, Add Page, Set Page Layers, and the rest — which invalidates the bake cache for you, then call Refresh on the display. Two traps:
  • Get Page hands you a copy. Editing it changes nothing until you Set Page it back.
  • Changing something no mutator covers (writing into Pages from C++, say) leaves the cache serving the old bake. Call Bump Revision first.
See Runtime Documents.

Next

Integration

Wiring Inkwell into the interaction system your project already has.

Rendering pipeline

The bake, the material contract, the cache, and what page resolution costs.

Blueprint API

Every node, property, event and struct with real types and defaults.

Runtime documents

Building and rewriting pages while the game runs.