BodySlide and Outfit Studio, a tool to convert, create, and customize outfits and bodies for The Elder Scrolls, Fallout and Starfield.
This fork adds portable Linux builds (an AppImage and a relocatable .tar.zst, both
produced by the Linux Release workflow) and a set
of environment variables so a launcher or mod manager can drive the programs without the
user having to configure anything in the GUI.
Each release publishes two x86_64 artifacts. Both come out of a single deployment, so they bundle exactly the same libraries and behave identically once running - only the packaging differs:
| Artifact | Use it when |
|---|---|
BodySlide-and-Outfit-Studio-<version>-x86_64.AppImage |
Running it yourself on a desktop. One file, nothing to extract, updatable through the published .zsync. |
BodySlide-and-Outfit-Studio-<version>-x86_64.tar.zst |
Driving it from a mod manager, or on any host where the AppImage cannot mount itself. |
Neither needs wxWidgets, GLEW or mesa installed. The bundles carry their own loader and glibc, so the (bleeding-edge) glibc they are built against does not become the compatibility floor: they run on SteamOS, Arch, Debian, Fedora and Ubuntu alike.
Both bundles ship BodySlide and Outfit Studio together rather than as two packages - BodySlide can launch Outfit Studio, and keeping them in one bundle both halves the download and lets that button keep working.
Prefer this one for mod managers, and for sandboxed or locked-down hosts.
The AppImage mounts itself with FUSE, which is not available inside a Flatpak sandbox
(no /dev/fuse, no fusermount3), in minimal containers, or on hosts where /tmp is
mounted noexec. The tarball is a plain directory tree and has no such requirement.
It is also the better shape to deploy outfits into: the extracted root is writable and
doubles as the data directory, so SliderSets/, ShapeData/, Config.xml and the logs
all live in one place, with no read-only mount and no writable-directory indirection.
tar --zstd -xf BodySlide-and-Outfit-Studio-*.tar.zst
cd BodySlide-and-Outfit-Studio-*-x86_64/
./BodySlide # or ./OutfitStudiotar --zstd needs the zstd program on PATH; without it, use
zstd -dc BodySlide-and-Outfit-Studio-*.tar.zst | tar -x instead.
The extracted directory looks like this:
BodySlide-and-Outfit-Studio-<version>-x86_64/
├── BodySlide launcher script - start here
├── OutfitStudio launcher script
├── bin/ the real executables, plus bundled helpers
├── res/ lang/ shaders, XRC layouts, reference meshes, translations
├── Config.xml ... the five XML config files, seeded from the defaults
└── (bundled libraries)
Start the programs through the two launcher scripts at the root, not the binaries in
bin/ - the launchers are what set up BSOS_APPDIR, BSOS_BINDIR and PATH for the
bundle. They resolve their own location through symlinks, so a symlink from
~/.local/bin/BodySlide works, and the whole tree is relocatable: move it anywhere, or
copy it to another machine, and it still runs.
The tarball deliberately ships without empty SliderSets/ShapeData directories.
See A note on project discovery below before creating
them - their presence changes where outfits are looked for.
Run it with --appimage-extract-and-run (or set URUNTIME_EXTRACT_AND_RUN=1). That
skips the FUSE mount and unpacks to a temporary directory instead, at the cost of a
slower start. If that also fails, the host most likely has /tmp mounted noexec or the
AppImage sits on a filesystem that cannot carry the executable bit (NTFS, exFAT) - use
the tarball.
| Variable | Purpose |
|---|---|
BSOS_APPDIR |
Data directory: Config.xml, Log_BS.txt / Log_OS.txt, res/, lang/, and - when it contains a SliderSets directory - the project data. Defaults to the directory holding the executable, which is what the tarball launchers use (the extracted root); the AppImage defaults it to ${XDG_DATA_HOME:-~/.local/share}/BodySlide instead, because its own directory is a read-only mount. |
BSOS_BINDIR |
Directory to launch sibling executables from, used by BodySlide's "Outfit Studio" button. Set this when the data directory holds no executables (AppImage) or when the programs must be started through a wrapper rather than as raw ELF binaries. Defaults to the executable's own directory. |
BSOS_TARGET_GAME |
Game to target. Accepts a name from the list below (case-insensitive) or the raw index. An unrecognised value is logged as a warning and ignored, rather than silently selecting the wrong game. |
BSOS_GAME_DATA_PATH |
The game's Data directory. Also written to the per-game slot the settings dialog keeps, so switching game in the UI and back does not lose it. |
BSOS_OUTPUT_DATA_PATH |
Where built meshes are written. Defaults to BSOS_GAME_DATA_PATH; set it separately to capture build output into a mod folder instead of dropping it into the game. |
BSOS_MSAA |
Maximum preview antialiasing samples: 0 (off), 2, 4, 8, or 16. Overrides Rendering/MSAASamples in Config.xml for this launch. Defaults to 16; unsupported levels fall back to lower ones. Restart both programs after changing it. |
BSOS_DIAGNOSTICS |
Set to 1 to log selection, preview-loading, and rendering timings to Log_BS.txt / Log_OS.txt. Requires LogLevel set to 3 in Config.xml. Leave unset for normal use. |
Valid BSOS_TARGET_GAME names (index in parentheses): Fallout3 (0),
FalloutNewVegas (1), Skyrim (2), Fallout4 (3), SkyrimSpecialEdition (4),
Fallout4VR (5), SkyrimVR (6), Fallout76 (7), Oblivion (8), Starfield (9).
The three game/path variables are applied at startup, before anything reads the configuration, and win over the stored settings on every launch. A value edited in the settings dialog is therefore reverted the next time the launcher starts the program. That is deliberate: the launcher is authoritative. Leave a variable unset to let the user control that setting through the GUI as usual.
Run against a Skyrim SE install, writing build output into a mod folder:
export BSOS_TARGET_GAME=SkyrimSpecialEdition
export BSOS_GAME_DATA_PATH="$HOME/Games/Skyrim Special Edition/Data"
export BSOS_OUTPUT_DATA_PATH="$HOME/mods/BodySlide Output"
./BodySlide-and-Outfit-Studio-*.AppImageTwo instances out of one AppImage, each with its own settings and logs:
BSOS_APPDIR="$HOME/.local/share/BodySlide/skyrim" \
BSOS_TARGET_GAME=SkyrimSpecialEdition \
BSOS_GAME_DATA_PATH="$HOME/Games/Skyrim Special Edition/Data" \
./BodySlide-and-Outfit-Studio-*.AppImageStart Outfit Studio instead of BodySlide - pass --outfit-studio (or OutfitStudio) as
the first argument, or symlink/rename the AppImage to OutfitStudio:
./BodySlide-and-Outfit-Studio-*.AppImage --outfit-studioThe examples use the AppImage, but the variables work the same way for the tarball -
substitute ./BodySlide for the AppImage in each one. The tarball has no
--outfit-studio argument because it does not need one: run ./OutfitStudio directly.
Note that its BSOS_APPDIR defaults to the extracted directory rather than
~/.local/share/BodySlide, so the two-instance example above is what you want if you
run one shared install against several games.
When given a separate BSOS_APPDIR, the tarball launcher creates it, links res/ and
lang/ to the bundle, and copies missing XML configuration files from the bundle root.
Existing configuration files and real resource directories are preserved.
Enable timings for a run that reproduces the slowdown:
BSOS_DIAGNOSTICS=1 ./BodySlide
# Or: BSOS_DIAGNOSTICS=1 ./OutfitStudioCollect Log_BS.txt / Log_OS.txt from BSOS_APPDIR. They include the OpenGL vendor,
renderer and version once the preview is initialized. The timing entries distinguish
selection work, waiting for a preview load, drawing, and swapping buffers. Drawing
timings measure CPU submission and driver waits, not GPU execution time.
Compare separate runs with the preview closed, with BSOS_MSAA=0, and with
GDK_BACKEND=x11 (where X11 or XWayland is available). Keep the same project and
other settings for each comparison. These variables can also prefix an AppImage
launch. Report the bundle version, GPU, display session, and whether it was launched
inside a Flatpak sandbox along with the logs; timings alone do not establish a
graphics-driver problem.
Outfits are found by ProjectUtil::GetProjectPath(), which checks, in order: a configured
ProjectPath, then <BSOS_APPDIR>/SliderSets, then <game data>/CalienteTools/BodySlide
and <game data>/Tools/BodySlide.
The presence of SliderSets in the data directory is a signal, not just storage: it means
"this is the project directory", and discovery stops there. So the packages deliberately
ship without an empty SliderSets - creating one would hijack discovery and leave
BodySlide listing nothing, even when a mod manager has deployed outfits to the game's
CalienteTools/BodySlide folder. Create it yourself (or let a mod manager create it) only
when you actually want a self-contained setup under BSOS_APPDIR.
- Building on Windows (Visual Studio or CMake)
- Building on Linux
- Installation and Settings
- Installation on Linux
Created by and/or with the help of:
- Caliente
- ousnius
- jonwd7 for NIF and general help
- degenerated1123 for help with shaders
- NifTools team
Relevant work:
- nifly: C++ NIF library for the NetImmerse File Format (NetImmerse, Gamebryo, Creation Engine).
- NiflySharp: Native C# / .NET version of nifly that uses source generation based on nifxml.
- Faster HDT-SMP (FSMP): Skinned mesh physics for Skyrim SE/AE/VR, maintained by DaydreamingDay / DaymareOn, forked from Karonar1 and aers, from the original work by HydrogensaysHDT.
The physics preview in BodySlide and Outfit Studio is a port of its
hdtSkinnedMeshcore (GPL-3.0, like this project) so it simulates the same physics XML files with the same behavior. See src/physics/hdt/README.md for the port notes.
Libraries used:
- wxWidgets
- nifly
- OpenGL
- OpenGL Image (GLI)
- Simple OpenGL Image Library 2 (SOIL2)
- half - IEEE 754-based half-precision floating point library
- Miniball
- LZ4(F)
- TinyXML-2
- nlohmann/json
- fkYAML
- Catch2 v3 (optional tests)
- FSEngine (BSA/BA2 library)
- Autodesk FBX SDK (optional FBX import/export)
- Bullet (optional physics preview)