VRSFML: my Emscripten-ready fork of SFML

18 august 2024

In November 2021, motivated by a desire to contribute to the growth and modernization of a library close to my heart1, I joined the SFML team2 after volunteering to lead modernization efforts3.

Over the past few years, significant time and resources have been invested in the 3.x branch, targeting C++17 and modernizing both SFML’s implementation and API.

I greatly appreciate the contributions of everyone involved in the project. However, I felt that a different approach would better align with my vision, which is why I decided to create my own fork: VRSFML.

I’m excited to introduce VRSFML, a work-in-progress and experimental fork of SFML that brings several key improvements:

  1. Modern OpenGL and first-class support for Emscripten4

  2. Enhanced API safety at compile-time

  3. Flexible design approach over strict OOP principles

  4. New audio API supporting multiple simultaneous devices

  5. Built-in SFML::ImGui module

  6. Remarkably fast compilation time & small run-time debug mode overhead

This article will delve into these features, explore the underlying design philosophy, and draw comparisons between the upcoming SFML 3.x release and my fork.

removal of legacy OpenGL and Emscripten support

A pivotal change in VRSFML, and the most challenging to research and implement, was the complete removal of legacy OpenGL calls (SFML 3.x still relies on calls such as glMatrixMode5). VRSFML upgrades the rendering pipeline to modern OpenGL, targeting OpenGL ES 3.0+ for mobile and web devices, and OpenGL 4.x+ for desktop platforms.

The primary motivation behind this significant change was to provide Emscripten6 support. I firmly believe that SFML’s adoption has been hindered by the inability to deploy applications to the web. In today’s landscape, users are hesitant to download executables for small 2D games or prototypes, and developers are reluctant to maintain extensive build matrices for multiple platforms unless absolutely necessary. Web deployment addresses these concerns, aligning VRSFML with other modern game development libraries.

You can now experience many SFML examples7 directly in your browser.

Importantly, user code requires no explicit #ifdef __EMSCRIPTEN__ directives. The only notable change is the use of an SFML_GAME_LOOP macro instead of a while (true) loop.

The implementation of Modern OpenGL and Emscripten support involves replacing the legacy SFML 3.x OpenGL pipeline with built-in shaders. The new shader interface is as follows:

Vertex Shader Fragment Shader
uniform mat4 sf_u_textureMatrix;
uniform mat4 sf_u_modelViewProjectionMatrix;

in vec2 sf_a_position;
in vec4 sf_a_color;
in vec2 sf_a_texCoord;

out vec4 sf_v_color;
out vec2 sf_v_texCoord;
in vec4 sf_v_color;
in vec2 sf_v_texCoord;

out vec4 sf_fragColor;



Despite advocating for the removal of legacy OpenGL and the inclusion of Emscripten support upstream8, these proposals were ultimately deemed out-of-scope for SFML 3.x by the rest of the team. This decision was made even though some developers had already implemented makeshift solutions9 for these features in SFML 2.x.

The team instead decided that it would be SFML 4.x’s focus to develop a completely new, modern rendering pipeline, potentially based on Vulkan. While this is an ambitious goal, I think that it does harm SFML’s userbase because (1) SFML 3.x will not bring significant improvements to the rendering pipeline or web support, potentially disappointing users who were expecting these updates, and (2) experienced SFML users might switch to alternative libraries rather than wait an indeterminate period for 4.x to be released.

prevent bugs: better APIs

A philosophy I really value in modern C++ libraries is “make invalid states unrepresentable”, preventing users from running into both logic and memory bugs. Combine that with the “code is read more than it is written” adage, and the principle of maximizing “local reasoning”10, and you’ll understand why I love APIs that leverage the C++ type system to improve safety and force users to think about error handling.

In June 2022, a GitHub issue11 was created to discuss the topic of “error handling” in SFML 3.x. After a lot of discussion and support from both the SFML team and users that tracked the issue12, it was decided that SFML 3.x would embrace algebraic data types as a way to make error handling obvious and explicit through C++’s type system. As SFML’s BDFL13 stated at the end of the issue in May 2024 (emphasis mine):

Closing this, as the direction has been decided and largely implemented. Feel free to reopen if there are still points you think need discussing.

Thus, a massive amount of work and effort14 was put into changing SFML 2.x’s error-prone API:

Legacy API
sf::Font font;
font.loadFromFile("arial.ttf"); // Can silently fail!

sf::Text text(font);
New API
const auto font = sf::Font::loadFromFile("arial.ttf").value();
sf::Text text(font);

The SFML team’s initial commitment to modernizing error handling and API safety in version 3.x was a significant step towards creating a more robust and user-friendly library. This approach, which leveraged factory functions returning std::optional<T>, brought several key benefits:

  1. Elimination of default empty states: SFML types became non-default-constructible, ensuring that objects were always in a valid state.

  2. Compile-time prevention of bugs and undefined behavior: The new design made it impossible to use uninitialized objects, a common source of errors in SFML 2.x.

  3. Forced consideration of failure cases: By returning std::optional<T>, APIs required users to explicitly handle potential failures.

  4. Explicit, visible error handling: The use of algebraic data types made error handling more apparent in the code, encouraging better practices.

  5. Simpler and faster implementation: Member function implementations of SFML types do not have to check/assert for the empty state.

This design also led to elegant and safe resource management. As an example, consider the task of loading a potentially-missing texture from user-generated content, reverting to a default texture in case of failure:

const auto texture = sf::Texture::loadFromFile(userTxPath).value_or(defaultTx);

However, despite my veto15, the SFML team’s decision in July 2024 to revert these changes and switch to exception-based error handling16 (reintroducing default constructors and empty states) represents, in my opinion, a significant regression in terms of API safety and modern C++ practices. This reversal:

  1. Reintroduces the possibility of invalid states, violating the “make invalid states unrepresentable” principle.

  2. Removes the forced consideration of failure cases in the type system, potentially leading to less robust code.

  3. Makes error handling implicit, reducing code readability and correctness.

  4. Misses an opportunity to educate users about modern C++ features like std::optional.

  5. Prevents SFML from being used in projects/platforms where exceptions are disabled/unavailable.

The justifications provided for this decision, such as users not being ready for std::optional or concerns about migration difficulty, seem to underestimate the capabilities of SFML users and prioritize ease of migration over substantial improvements to the library.

This reversal not only impacts the technical aspects of SFML but also its pedagogical value. As a library often used by beginners, SFML had an opportunity to introduce users to modern C++ practices and type-safe programming. By reverting to a more traditional but less safe API, this educational aspect has been diminished.

Ultimately, this decision appears to prioritize short-term convenience over long-term robustness and modernity. One of the SFML maintainers who approved the reversal described it17 quite concisely (emphasis mine):

The default constructor (of SFML types) is equivalent to the default constructor of std::optional. It leaves the object in an empty state that is basically unusable. This is how SFML has always been. Resources like sf::Font have an internal empty state so they’re secretly optionals in disguise.

These statements raise concerns about the potential implications for API safety and robustness. For comparison, here’s the SFML 3.x equivalent of the elegant VRSFML one-liner (shown above):

sf::Texture texture; // Empty state... need to be careful.
                     // Also, can't use `const`!

if (!texture.loadFromFile(userTxPath))
    texture = defaultTx;

VRSFML fully adopts the factory-based API design as a commitment to safer, more explicit programming practices:

// ERROR: Does not compile - VRSFML resources lack a default "empty state"
/* sf::SoundBuffer sb0; */

// OK: *Explicit* exception throwing on failure
const auto sb1 = sf::SoundBuffer::loadFromFile("ball.wav").value();

// OK: *Explicit* handling of failure case
const auto sb2 = sf::SoundBuffer::loadFromFile("ball.wav");
if (!optSoundBuffer.hasValue()) { return EXIT_FAILURE; }

// OK: *Explicit* fallback provision
const auto sb3 = sf::SoundBuffer::loadFromFile("ball.wav").valueOr(defSound);

VRSFML also applies this philosophy to APIs that SFML 3.x didn’t originally modify, such as sf::Window, sf::Image, and sf::Sprite.

solving the white square problem: lifetime tracking

The case of sf::Sprite is particularly noteworthy. In SFML 2.x and 3.x, sf::Sprite stores a raw pointer to sf::Texture, which can lead to lifetime issues:

auto makeSprite()
{
    sf::Texture texture(/* ... */);
    return sf::Sprite(texture);
}

someRenderTarget.draw(makeSprite()); // UNDEFINED BEHAVIOR!

This “raw pointer relationship” is not unsual in SFML, as one of the principles that the library follows is that users should decide how to manage their own resources. However, especially for beginners, many18 lifetime issues were caused by the texture associated to a sprite being destroyed prior to the sprite itself, resulting in undefined behavior and the dreaded “white square problem”19.

Upon investigating the issue, I devised a method to prevent a longstanding bug in SFML at compile-time. The sf::Sprite class only requires texture information at the time of rendering. Storing a sf::Texture* within the sprite is unnecessary, as it can be passed during the draw call. This approach ensures that a valid texture object must be provided by the caller at the point of the draw call, effectively eliminating the issue.

I anticipated that this solution would be well-received by the SFML team due to its technical merits. However, it was rejected despite its advantages, with arguments such as “this does not follow OOP principles” and “this is not future-proof, as when we switch to Vulkan, the sprite might need to know the texture at construction time”. The technical discussions and arguments can be found in #3072 and #3080, which document my two attempts at enhancing the safety of a fundamental SFML type for version 3.x.

My disappointment with the reception of this idea and the constant rejections is evident in these discussions. In retrospect, I acknowledge that my tone may have been influenced by emotion and frustration more than was appropriate for a technical discourse.

VRSFML implements my proposed solution, extending it to SFML shapes as well:

// ERROR: Does not compile - sprites no longer store a texture pointer
/* sf::Sprite sprite(texture); */

// OK: Prepare the sprite to eventually display the entire texture
sf::Sprite sprite(texture.getRect());

// ERROR: Does not compile - texture must be provided during the draw call
/* window.draw(sprite); */

// OK: texture available at the point of the draw call - no lifetime issues
window.draw(sprite, texture);

VRSFML takes this approach further by introducing a toggleable lightweight lifetime tracking system for all SFML resources. This system detects common lifetime mistakes between dependee types (e.g., sf::Font) and dependent types (e.g., sf::Text) at runtime, providing users with readable error messages.

(This comprehensive solution was also proposed for inclusion in SFML 3.x20 but was not accepted. Instead, #3122 was chosen, which addresses only textures and relies on undefined behavior.)

When lifetime tracking is enabled and a dependee-dependant relationship is broken, the following error message is displayed (alongside a stack trace, if SFML_ENABLE_STACK_TRACES is provided):

FATAL ERROR: a texture object was destroyed while existing sprite objects depended on it.

Please ensure that every texture object outlives all of the sprite objects associated with it, otherwise those sprites will try to access the memory of the destroyed texture, causing undefined behavior (e.g., crashes, segfaults, or unexpected run-time behavior).

One of the ways this issue can occur is when a texture object is created as a local variable in a function and passed to a sprite object. When the function has finished executing, the local texture object will be destroyed, and the sprite object associated with it will now be referring to invalid memory. Example:

sf::Sprite createSprite()
{
    sf::Texture texture(/* ... */);
    sf::Sprite sprite(texture, /* ... */);

    return sprite;
    //     ^^^^^^

    // ERROR: `texture` will be destroyed right after
    //        `sprite` is returned from the function!
}

Another possible cause of this error is storing both a texture and a sprite together in a data structure (e.g., class, struct, container, pair, etc…), and then moving that data structure (i.e., returning it from a function, or using std::move) – the internal references between the texture and sprite will not be updated, resulting in the same lifetime issue.

In general, make sure that all your texture objects are destroyed after all the sprite objects depending on them to avoid these sort of issues.

The message is dynamic and changes depending on the types of the SFML objects. The impact on the implementation of the SFML types themselves is minimal, e.g.:

// in `Font.hpp`, add:
SFML_DEFINE_LIFETIME_DEPENDEE(Font, Text);

// in `Text.hpp`, add:
SFML_DEFINE_LIFETIME_DEPENDANT(Font);

type-safe API: shader uniforms

In SFML 2.x and 3.x, shader uniform manipulation is achieved through various overloads of the following form:

void sf::Shader::setUniform(const std::string& name, bool x);
void sf::Shader::setUniform(const std::string& name, int x);
void sf::Shader::setUniform(const std::string& name, float x);
// ...

While functional, this API presents two significant issues:

  1. It doesn’t account for cases where name doesn’t refer to an actual shared uniform variable.

  2. Each setUniform call requires translating the name parameter to the uniform’s location (an integer).

VRSFML addresses these issues with an improved API. First, users must locate a uniform using this new member function:

std::optional<UniformLocation> getUniformLocation(std::string_view name);

The UniformLocation type is a strong typedef21 over an integer. Like most VRSFML types, it’s non-default-constructible and can only be created by sf::Shader. This ensures that possessing a UniformLocation object guarantees the existence of a real uniform location.

The previous overloads have been modified to accept a UniformLocation:

void sf::Shader::setUniform(UniformLocation location, bool x);
void sf::Shader::setUniform(UniformLocation location, int x);
void sf::Shader::setUniform(UniformLocation location, float x);
// ...

This design ensures that setUniform can only be called with a valid UniformLocation object in scope, implying that getUniformLocation must have been called and the returned std::optional successfully unwrapped.

fewer OOP hierarchies

SFML has historically embraced the Object-Oriented Programming (OOP) paradigm, even when it presented clear drawbacks. For instance, all SFML types that could be rendered derived from a polymorphic sf::Drawable base class and were required to implement a draw pure virtual member function:

virtual void draw(sf::RenderTarget&, const RenderStates&) const = 0;

While this may appear to be a logical decision, it exemplifies how attempting to map real-world concepts into a programming API can lead to several significant drawbacks:

  1. Encourages poor run-time performance: SFML users, particularly those new to C++, were implicitly encouraged to store multiple drawable objects homogeneously in containers like std::vector of std::shared_ptr<sf::Drawable>22. This approach is detrimental to performance and cache utilization. The absence of a common polymorphic base class would guide users towards a more appropriate data-oriented design.
  1. Promotes inheritance over composition: Users tend to compose multiple drawable objects by creating their own wrapper that also derives from sf::Drawable, simply to fit into the existing hierarchy. In many cases, a simple function or struct would suffice to group multiple drawables together more efficiently.

  2. Incompatible with efficient rendering solutions: The concept of rendering one object at a time is far removed from how GPUs actually function. Vertex batching is necessary for efficient use of modern hardware. Erasing the type of a drawable object would make the implementation of any batching API on top of existing SFML types quite difficult, whereas a batching API could be easily implemented for, e.g., a std::vector<sf::Sprite>.

  3. Results in suboptimal and potentially dangerous APIs: Consider the sf::Sprite and sf::Shape “white square problem” mentioned earlier. This issue only existed because these types had to conform to the sf::Drawable interface, which did not accept a sf::Texture during the draw call, necessitating its storage beforehand.

VRSFML eliminates the sf::Drawable class entirely and converts sf::Shape and sf::Transformable into non-polymorphic base classes, encouraging the adoption of a more efficient data-oriented design.

Notably, neither the original SFML examples nor the source code of Open Hexagon23 required adaptation to accommodate these changes, suggesting that the original class hierarchies may not have been necessary in the first place.

Note that, if needed, type erasure is nowadays trivial to achieve: a std::function<void(sf::RenderTarget&)> would easily provide the ability to draw anything polymorphically.

Furthermore, sf::VertexArray has been replaced with std::vector<sf::Vertex>. The former was essentially a thin wrapper around the latter, existing primarily to conform to the sf::Drawable interface. Useful vector APIs such as reserve or emplace_back were not available on sf::VertexArray, making it a less advantageous choice compared to std::vector<sf::Vertex> without any discernible benefits. This modification was proposed for upstream SFML but was rejected strongly24.

all the audio devices, at the same time

VRSFML has served as a playground for simplifying SFML’s implementation, with a focus on minimizing hidden global state. In SFML 2.x and 3.x, all graphical objects inherit from sf::GlResource (which internally owns a std::shared_ptr<void> to a shared OpenGL context), and all audio objects inherit from sf::AudioResource (which internally owns a std::shared_ptr<void> to a shared miniaudio device).

That design primarily supports globally defined SFML objects, which I don’t want to encourage. VRSFML completely removes sf::GlResource and sf::AudioResource. Instead, users create sf::GraphicsContext and sf::AudioContext “manager objects” at the start of main (or at an appropriate point). These context manager objects hold the state previously shared between objects. This change led to numerous simplifications in SFML’s internals, including the elimination of transient OpenGL contexts.

Operations requiring an active OpenGL context now need a sf::GraphicsContext& parameter. This includes loading textures, fonts, or creating windows. While seemingly less ergonomic, it’s not problematic in practice if the application is designed to pass the graphics context from main to its callees. This requirement ensures that any graphical operation will always have an OpenGL context by construction order, eliminating the need for transient contexts. For example:

// Create graphics context
sf::GraphicsContext graphicsCtx;

// Create a render window
sf::RenderWindow window(graphicsCtx,
                        {.size  = {1024u, 768u},
                         .title = "SFML Example",
                         .style = sf::Style::Titlebar | sf::Style::Close});

// Load a graphical resource
const auto logoTx = sf::Texture::loadFromFile(graphicsCtx, "logo.png").value();

(Note the new API for window settings, utilizing C++20’s designated initializers feature.)

The principle for sf::AudioContext is similar, but this simplification led to extending the API to support multiple simultaneous devices. While SFML 3.x allows setting the “current active audio device”, users can’t play different sounds through different devices simultaneously. VRSFML provides this functionality with an elegant, type-safe API:

// Load audio resources
auto music0 = sf::Music::openFromFile("doodle_pop.ogg").value();
auto music1 = sf::Music::openFromFile("ding.flac").value();

// Create audio context
auto audioContext = sf::AudioContext::create().value();

// Query hardware audio device handles
const auto deviceHandles =
    sf::AudioContextUtils::getAvailablePlaybackDeviceHandles(audioContext);

// (Assuming there are at least two available playback devices)
const sf::PlaybackDeviceHandle& pdh0 = deviceHandles.at(0);
const sf::PlaybackDeviceHandle& pdh1 = deviceHandles.at(1);

// Create actual playback devices for the queried handles
sf::PlaybackDevice pd0(audioContext, pdh0);
sf::PlaybackDevice pd1(audioContext, pdh1);

// Simultaneously play the two musics on two different devices
music0.play(pd0);
music1.play(pd1);

I presented the idea of supporting multiple simultaneous devices when SFML 3.x audio-related changes were being discussed. The response25 was:

This is a very very advanced topic, one so advanced that not many AAA games even dare to support simultaneously outputting different audio streams to different audio playback devices. While technically possible, this would require an entire redesign of the sfml-audio public API, and that is definitely out of scope for this PR and SFML 3.

dear imgui, out of the box

Omar Cornut’s widely popular Dear ImGui26 library has long been compatible with SFML through Elias Daler’s imgui-sfml27 compatibility layer. During the development of SFML 3.x and VRSFML, updating imgui-sfml’s internals to match SFML’s evolving API proved challenging. Managing this additional dependency in real SFML projects like Open Hexagon was also cumbersome.

To address these issues, imgui-sfml has been incorporated as an “official” VRSFML module: SFML::ImGui. Like other SFML modules, users can choose whether to build it (alongside the Dear ImGui dependency). The internals of imgui-sfml have also been updated to use a modern OpenGL backend, supporting Emscripten.

Hidden global state has been minimized and encapsulated in a sf::ImGui::ImGuiContext object, which can be created in main similarly to sf::GraphicsContext and sf::AudioContext. The following example demonstrates its usage:

sf::GraphicsContext     graphicsCtx;
sf::ImGui::ImGuiContext imGuiCtx(graphicsCtx);

sf::RenderWindow window(graphicsCtx,
    {.size{1024u, 768u}, .title = "ImGui + SFML = <3"});

if (!imGuiCtx.init(window))
    return -1;

sf::Clock deltaClock;

SFML_GAME_LOOP
{
    while (const sf::base::Optional event = window.pollEvent())
        imGuiCtx.processEvent(window, *event);

    imGuiCtx.update(window, deltaClock.restart());

    ImGui::ShowDemoWindow();

    window.clear();
    imGuiCtx.render(window);
    window.display();
};

moving away from the Standard Library

This change is likely to be controversial. I highly value rapid iteration times when developing software, particularly games. To achieve a satisfying and productive code-compile-test loop, a library must compile quickly and have minimal debug run-time overhead. It’s reasonable to assume that a significant portion of the game development community shares these priorities. Improving compilation times has been a subject of great interest to me, which I’ve thoroughly researched, as demonstrated by my well-received talk on the topic28.

Motivated by the game development community’s frustration with modern C++, I’ve also conducted in-depth research on the impact of C++ abstractions on debug performance. This is evidenced by my September 2022 article29, which was also well-received and prompted changes in all three major compilers to address some of the issues raised.

Unfortunately, primarily due to historical and backward-compatibility constraints, all major C++ standard library implementations fall short of the compilation speed and debug performance standards I consider acceptable. Explaining why standard library headers are so heavyweight and why standard library abstractions are buried under numerous layers of templates would require an extensive article of its own.

It’s important to emphasize that this situation is not due to incompetence or carelessness on the part of the dedicated volunteers who work on these fundamental components of software engineering. Rather, it’s a result of a long and complex history of the C++ language and its standard library.

Given these constraints, I’ve made the decision to reduce dependencies on the C++ standard library in VRSFML by introducing SFML::Base. This new module provides essential utilities including: type traits, smart pointers, math functions, assertions, PImpl wrappers, basic algorithms, and much more.

These utilities are meticulously designed to:

  1. Minimize header dependencies

  2. Reduce the number of template instantiations

  3. Decrease the depth of the call stack

Annotations like [[gnu::always_inline]] are employed to minimize run-time overhead of basic abstractions in debug mode. Additionally, numerous (opinionated) quality-of-life improvements have been implemented in the API and functionality of SFML::Base types compared to their standard counterparts.

VRSFML utilizes its own vocabulary types in the public API, such as:

  • base::Optional<T> instead of std::optional<T>

  • base::UniquePtr<T> instead of std::unique_ptr<T>

While this was a challenging decision, benchmarking and testing have demonstrated that the benefits in compilation time and debug performance justify this approach. I do intend to revisit this decision in the future if necessary, and potentially revert to the standard library or enable users of the library to choose their preferred types at configuration-time.

other various improvements

  • Optimized sf::Shader source loading by reading into a thread-local vector, enhancing performance.

  • Improved sf::Text rendering with outlines, reducing it to a single draw call compared to the upstream SFML’s two draw calls.

  • Made sf::priv::err thread-safe and more ergonomic, eliminating the need for std::endl.

  • Verified that all factory functions support RVO or NRVO, as confirmed by GCC’s -Wnrvo flag.

  • Added the useful sf::Vector2<T>::movedTowards(T, Angle) function.

  • sf::Color, sf::Vertex, sf::Vector2, sf::Vector3, and sf::Rect are now aggregate types.

  • Updated sf::Socket constructor to include a mandatory isBlocking parameter, ensuring users are aware of blocking behavior.

  • Removed catch-all headers such as SFML/Audio.hpp to promote good header hygiene in user projects.

Q/A

How do I get started with VRSFML?

  • There is no official documentation yet, and many APIs are still undocumented. Still, I believe it’s quite easy to get started by looking at the existing examples and experimenting with them. The SFML documentation is also an incredibly valuable resource, but do keep in mind that many APIs have changed in VRSFML.

Is VRSFML a direct competitor to SFML?

  • I don’t consider VRSFML a competitor to SFML. Many of the changes in VRSFML are quite opinionated and specifically tailored for more experienced C++ and SFML developers. Broadly speaking, SFML 3.x seems to target beginner C++ developers and is well-suited for smaller games, applications, or prototypes. In contrast, VRSFML is geared towards more experienced C++ developers and is better suited for larger games or applications that require scalability and robustness.

    Additionally, I view VRSFML as a valuable testing ground for features and changes that might eventually be incorporated into upstream SFML. The freedom to experiment and break things without the need for a formal PR review process has allowed me to push boundaries and explore new ideas. Perhaps the SFML team will find some of these ideas worth integrating into upstream SFML.

Are you still part of the SFML team?

  • Yes, I am. However, I’ve reduced the time I spend contributing to SFML to focus on my fork and other projects. While I remain interested in contributing to SFML and influencing its future direction, I intend to limit my contributions to smaller, less controversial changes to avoid lengthy technical debates or disagreements.

Is VRSFML production-ready?

  • No, VRSFML is still a work in progress with some issues that need to be resolved. Additionally, it has only been tested only with GCC and Clang, and only on Windows x64, Windows ARM, Linux x64, and Emscripten. Mobile devices have not been tested, and some mobile-specific and MSVC-specific code adjustments are necessary. Contributions are welcome!

What are the future plans for VRSFML?

  • I don’t know yet. I’m interested in keeping the fork up-to-date with upstream, and in writing a nice small example game that runs on the browser to showcase Emscripten support. Apart from that, my future effort on the project will depend on the amount of interest it generates in the open-source community and on my personal future game development efforts using the library.

shameless self-promotion


  1. SFML has been an integral part of my journey in learning C++, starting over 11 years ago!↩︎

  2. https://en.sfml-dev.org/forums/index.php?topic=28324.0↩︎

  3. https://en.sfml-dev.org/forums/index.php?topic=28301.0↩︎

  4. https://emscripten.org/↩︎

  5. SFML/Graphics/RenderTarget.cpp#L538-L543↩︎

  6. https://emscripten.org/↩︎

  7. You will notice that these original SFML examples are a bit lackluster – I plan to create more engaging ones in the future.↩︎

  8. These discussions were had in SFML’s Discord (#contributing channel).↩︎

  9. E.g. ZombiesChannel’s fork↩︎

  10. “Local reasoning is the idea that the reader can make sense of the code directly in front of them, without going on a journey discovering how the code works” (source)↩︎

  11. https://github.com/SFML/SFML/issues/2139↩︎

  12. Positive GitHub reactions from non–SFML-team-members were given in issue comments in favour of ADT-based error handling, alongside positive feedback on Discord.↩︎

  13. “Benevolent Dictator For Life”↩︎

  14. For a non-exhaustive list of PRs, see #3088’s description.↩︎

  15. SFML/pull/3152#issuecomment-2228238814↩︎

  16. https://github.com/SFML/SFML/pull/3152↩︎

  17. These discussions were had in SFML’s Discord (#contributing channel).↩︎

  18. It is sufficient to search online for “white square SFML” to find several reports of this issue.↩︎

  19. https://www.sfml-dev.org/tutorials/2.6/graphics-sprite.php#the-white-square-problem↩︎

  20. https://github.com/SFML/SFML/pull/3097↩︎

  21. https://arne-mertz.de/2016/11/stronger-types/↩︎

  22. GitHub search for std::shared_ptr<sf::Drawable>↩︎

  23. Open Hexagon is a commercial open-source game I created using VRSFML, available on Steam↩︎

  24. https://github.com/SFML/SFML/pull/3118↩︎

  25. SFML/pull/3029#issuecomment-2131745897↩︎

  26. https://www.dearimgui.com/↩︎

  27. https://github.com/SFML/imgui-sfml↩︎

  28. https://www.youtube.com/watch?v=PfHD3BsVsAM↩︎

  29. debug_performance_cpp article↩︎


RSS Feed