diff --git a/packages/openbabel/package.py b/packages/openbabel/package.py
new file mode 100644
index 0000000000000000000000000000000000000000..37be524cc8f9defeea12ee4ae805426267fa7aa5
--- /dev/null
+++ b/packages/openbabel/package.py
@@ -0,0 +1,92 @@
+# Copyright 2013-2023 Lawrence Livermore National Security, LLC and other
+# Spack Project Developers. See the top-level COPYRIGHT file for details.
+#
+# SPDX-License-Identifier: (Apache-2.0 OR MIT)
+
+from spack.package import *
+
+
+class Openbabel(CMakePackage):
+    """Open Babel is a chemical toolbox designed to speak the many languages
+    of chemical data. It's an open, collaborative project allowing anyone to
+    search, convert, analyze, or store data from molecular modeling, chemistry,
+    solid-state materials, biochemistry, or related areas."""
+
+    homepage = "https://openbabel.org/wiki/Main_Page"
+    url = "https://github.com/openbabel/openbabel/archive/openbabel-3-0-0.tar.gz"
+    git = "https://github.com/openbabel/openbabel.git"
+
+    maintainers("RMeli")
+
+    version("master", branch="master")
+    version("3.1.1", tag="openbabel-3-1-1")
+    version("3.1.0", tag="openbabel-3-1-0")
+    version("3.0.0", tag="openbabel-3-0-0")
+    version("2.4.1", tag="openbabel-2-4-1")
+    version("2.4.0", tag="openbabel-2-4-0")
+
+    variant("python", default=True, description="Build Python bindings")
+    variant("gui", default=True, description="Build with GUI")
+    variant("cairo", default=True, description="Build with Cairo (PNG output support)")
+    variant("openmp", default=False, description="Build with OpenMP")
+    variant("maeparser", default=False, description="Built with MAE parser")
+    variant("coordgen", default=False, description="Build with Coordgen")
+
+    extends("python", when="+python")
+
+    depends_on("python", type=("build", "run"), when="+python")
+    depends_on("cmake@3.1:", type="build")
+    depends_on("pkgconfig", type="build")
+    depends_on("swig@2.0:", type="build", when="+python")
+
+    depends_on("boost +filesystem +iostreams +test")
+    depends_on("cairo", when="+cairo")  # required to support PNG depiction
+    depends_on("pango", when="+cairo")  # custom cairo requires custom pango
+    depends_on("eigen@3.0:")  # required if using the language bindings
+    depends_on("libxml2")  # required to read/write CML files, XML formats
+    depends_on("zlib")  # required to support reading gzipped files
+    depends_on("rapidjson")  # required to support JSON
+    depends_on("libsm")
+    depends_on("uuid")
+
+    depends_on("maeparser", when="+maeparser")
+    depends_on("coordgen", when="+coordgen")
+
+    # Needed for Python 3.6 support
+    patch("python-3.6-rtld-global.patch", when="@:2.4.1+python")
+
+    # Convert tabs to spaces. Allows unit tests to pass
+    patch("testpdbformat-tabs-to-spaces.patch", when="@:2.4.1")
+
+    def cmake_args(self):
+        spec = self.spec
+        args = []
+
+        if "+python" in spec:
+            args.extend(
+                [
+                    "-DPYTHON_BINDINGS=ON",
+                    "-DPYTHON_EXECUTABLE={0}".format(spec["python"].command.path),
+                    "-DRUN_SWIG=ON",
+                ]
+            )
+        else:
+            args.append("-DPYTHON_BINDINGS=OFF")
+
+        args.append(self.define_from_variant("BUILD_GUI", "gui"))
+        args.append(self.define_from_variant("ENABLE_OPENMP", "openmp"))
+        args.append(self.define_from_variant("WITH_MAEPARSER", "maeparser"))
+        args.append(self.define_from_variant("WITH_COORDGEN", "coordgen"))
+
+        return args
+
+    @run_after("install")
+    @on_package_attributes(run_tests=True)
+    def check_install(self):
+        obabel = Executable(join_path(self.prefix.bin, "obabel"))
+        obabel("-:C1=CC=CC=C1Br", "-omol")
+
+        if "+python" in self.spec:
+            python("-c", "import openbabel")
+            if self.spec.version < Version("3.0.0"):
+                python("-c", "import pybel")
diff --git a/packages/openbabel/python-3.6-rtld-global.patch b/packages/openbabel/python-3.6-rtld-global.patch
new file mode 100644
index 0000000000000000000000000000000000000000..68cd56a1f5c7dd66df0f76598b81a098c84d3f14
--- /dev/null
+++ b/packages/openbabel/python-3.6-rtld-global.patch
@@ -0,0 +1,42 @@
+The DLFCN module has been removed from python 3.6, as it is not
+documented. Same funtionality can be achive with the os module
+that makes available the os.RTLD_GLOBAL variable for dlopen()
+
+See https://github.com/openbabel/openbabel/pull/372 for the
+source of this patch. The original patch only affects the CMake
+file that SWIG uses to generate openbabel.py. This patch also
+includes changes to openbabel.py.
+
+diff -Nuar a/scripts/CMakeLists.txt b/scripts/CMakeLists.txt
+--- a/scripts/CMakeLists.txt	2017-05-17 10:02:54.408527942 -0500
++++ b/scripts/CMakeLists.txt	2017-05-17 10:04:09.701598715 -0500
+@@ -81,11 +81,8 @@
+             COMMAND ${SWIG_EXECUTABLE} -python -c++ -small -O -templatereduce -naturalvar -I${openbabel_SOURCE_DIR}/include -I${openbabel_BINARY_DIR}/include -o ${openbabel_SOURCE_DIR}/scripts/python/openbabel-python.cpp ${eigen_define} -outdir ${openbabel_SOURCE_DIR}/scripts/python ${openbabel_SOURCE_DIR}/scripts/openbabel-python.i
+               COMMAND ${CMAKE_COMMAND} -E echo "import sys" > ob.py
+               COMMAND ${CMAKE_COMMAND} -E echo "if sys.platform.find('linux'\) != -1:" >> ob.py
+-              COMMAND ${CMAKE_COMMAND} -E echo "  try:" >> ob.py
+-              COMMAND ${CMAKE_COMMAND} -E echo "    import dl" >> ob.py
+-              COMMAND ${CMAKE_COMMAND} -E echo "  except ImportError:" >> ob.py
+-              COMMAND ${CMAKE_COMMAND} -E echo "    import DLFCN as dl" >> ob.py
+-              COMMAND ${CMAKE_COMMAND} -E echo "  sys.setdlopenflags(sys.getdlopenflags() | dl.RTLD_GLOBAL)" >> ob.py
++              COMMAND ${CMAKE_COMMAND} -E echo "  import os" >> ob.py
++              COMMAND ${CMAKE_COMMAND} -E echo "  sys.setdlopenflags(sys.getdlopenflags() | os.RTLD_GLOBAL)" >> ob.py
+               COMMAND cat ${openbabel_SOURCE_DIR}/scripts/python/openbabel.py >> ob.py
+               COMMAND ${CMAKE_COMMAND} -E copy ob.py ${openbabel_SOURCE_DIR}/scripts/python/openbabel.py
+               COMMAND ${CMAKE_COMMAND} -E remove ob.py
+diff -Nuar a/scripts/python/openbabel.py b/scripts/python/openbabel.py
+--- a/scripts/python/openbabel.py	2017-05-17 10:02:54.398527534 -0500
++++ b/scripts/python/openbabel.py	2017-05-17 10:04:26.705292138 -0500
+@@ -1,10 +1,7 @@
+ import sys
+ if sys.platform.find('linux') != -1:
+-  try:
+-    import dl
+-  except ImportError:
+-    import DLFCN as dl
+-  sys.setdlopenflags(sys.getdlopenflags() | dl.RTLD_GLOBAL)
++  import os
++  sys.setdlopenflags(sys.getdlopenflags() | os.RTLD_GLOBAL)
+ # This file was automatically generated by SWIG (http://www.swig.org).
+ # Version 3.0.10
+ #
diff --git a/packages/openbabel/testpdbformat-tabs-to-spaces.patch b/packages/openbabel/testpdbformat-tabs-to-spaces.patch
new file mode 100644
index 0000000000000000000000000000000000000000..0a71a72e014ad1b5cd895599c97cfa6e66eef582
--- /dev/null
+++ b/packages/openbabel/testpdbformat-tabs-to-spaces.patch
@@ -0,0 +1,47 @@
+From 08cd38485d4cf1df8802da540f3018921dbc735e Mon Sep 17 00:00:00 2001
+From: "Adam J. Stewart" <ajstewart426@gmail.com>
+Date: Wed, 17 May 2017 10:56:23 -0500
+Subject: [PATCH] Convert tabs to spaces in testpdbformat.py
+
+See https://github.com/openbabel/openbabel/pull/1568
+
+---
+ test/testpdbformat.py | 12 ++++++------
+ 1 file changed, 6 insertions(+), 6 deletions(-)
+
+diff --git a/test/testpdbformat.py b/test/testpdbformat.py
+index 40bd316..ceb8496 100644
+--- a/test/testpdbformat.py
++++ b/test/testpdbformat.py
+@@ -24,12 +24,12 @@ class TestPDBFormat(BaseTest):
+ 
+     def testInsertionCodes(self):
+         """
+-	Testing a PDB entry with insertion codes to distinguish residues
+-	upon conversion to FASTA.
++        Testing a PDB entry with insertion codes to distinguish residues
++        upon conversion to FASTA.
+         """
+         self.canFindExecutable("babel")
+ 
+-	self.entryPDBwithInsertioncodes="""ATOM    406  N   VAL L  29      58.041  17.797  48.254  1.00  0.00           N  
++        self.entryPDBwithInsertioncodes="""ATOM    406  N   VAL L  29      58.041  17.797  48.254  1.00  0.00           N  
+ ATOM    407  CA  VAL L  29      57.124  18.088  47.170  1.00  0.00           C  
+ ATOM    408  C   VAL L  29      55.739  17.571  47.538  1.00  0.00           C  
+ ATOM    409  O   VAL L  29      55.535  16.362  47.550  1.00  0.00           O  
+@@ -100,9 +100,9 @@ ATOM    473  HE1 TYR L  32      48.512  15.775  42.066  1.00  0.00           H
+ ATOM    474  HE2 TYR L  32      48.145  19.172  44.648  1.00  0.00           H  
+ ATOM    475  HH  TYR L  32      46.462  17.658  44.280  1.00  0.00           H  
+ """
+-	output, error = run_exec(self.entryPDBwithInsertioncodes,
+-				     "babel -ipdb -ofasta")
+-	self.assertEqual(output.rstrip().rsplit("\n",1)[1], "VSSSY")
++        output, error = run_exec(self.entryPDBwithInsertioncodes,
++                                     "babel -ipdb -ofasta")
++        self.assertEqual(output.rstrip().rsplit("\n",1)[1], "VSSSY")
+ 
+ if __name__ == "__main__":
+     testsuite = []
+-- 
+2.9.4
+