diff --git a/hocon.html.markdown b/hocon.html.markdown
new file mode 100644
index 00000000..67ea30a2
--- /dev/null
+++ b/hocon.html.markdown
@@ -0,0 +1,574 @@
+---
+language: hocon
+filename: learnhocon.conf
+contributors:
+- [TehBrian, 'https://tehbrian.xyz']
+---
+
+Human-Optimized Configuration Object Notation, or HOCON, is a configuration and
+data serialization format designed to be easily editable by humans.
+
+It's a superset of JSON, meaning that any valid JSON is valid HOCON, but it
+differs in being much less pedantic and opinionated. With its flexible yet
+easily determinable syntax, resulting configuration files are often much less
+noisy than some other formats.
+
+Additionally, its support for comments makes it much better suited for
+user-facing configurations than JSON.
+
+```hocon
+// Comments can either look like this,
+# or they can look like this.
+// Anything after // or # is a comment.
+
+##################
+### THE BASICS ###
+##################
+
+# Everything in HOCON is either a key, a value, or a separator.
+# : and = are separators. They separate the key from the value.
+key: value
+another_key = another_value
+
+# You can use either separator with or without whitespace on either side.
+colon1:value
+colon2: value
+colon3 : value
+equals1=value
+equals2= value
+equals3 = value
+# As you'll see, HOCON is very nonrestrictive regarding its syntax style.
+
+# HOCON also isn't opinionated on how keys look.
+THIS_IS_A_VALID_KEY: value
+this-is-also-a-valid-key: value
+keys can have spaces too: value
+or even numbers like 12345: value
+"you can even quote keys if you'd like!": value
+
+# A key, followed by any separator, and then finally a value, is called a field.
+this_entire_line_is: a field
+
+###################
+### VALUE TYPES ###
+###################
+
+# The types that a value can be are string, number, object, array, boolean, and
+# null. Every value type except for array and object are called simple values.
+
+## SIMPLE VALUES ##
+
+quoted_string: "I like quoting my strings."
+unquoted_string: I don't like quoting my strings.
+# Special characters that cannot be used in unquoted strings are:
+# $ " { } [ ] : = , + # ` ^ ? ! @ * &
+# Unquoted strings do not support any kind of escaping. If using one of those
+# special characters is desired, use a quoted string.
+multi-line_string: """
+ This entire thing is a string!
+ One giant, multi-line string.
+ You can put 'single' and "double" quotes without it being invalid.
+"""
+
+number: 123
+negative: -123
+fraction: 3.1415926536
+scientific_notation: 1.2e6 // same as 1.2 * (10^6)
+
+boolean: true # or false
+empty: null
+
+## ARRAYS ##
+
+# Arrays hold lists of values.
+# Values in arrays can be separated with commas..
+array: [ 1, 2, 3, 4, 5 ]
+fibonacci: [1,1,2,3,5,8,13]
+multiples_of_5: [5, 10, 15, 20,] # Notice the trailing comma. That's okay here.
+# or newlines..
+friends: [
+ "Brian"
+ "Sophie"
+ "Maya"
+ "Sabina"
+]
+# or both!
+ingredients: [
+ "Egg",
+ "Sugar",
+ "Oil",
+ "Flour", # Notice the trailing comma. That's okay here too.
+]
+# Once again, HOCON has a very loose syntax. Use whichever style you prefer.
+no newline before or after bracket: ["This"
+ "is"
+ "an"
+ "array!"]
+
+# Just like any other value, arrays can hold other arrays.
+array in array: [ [1, 2, 3], ["a", "b", "c"] ]
+array in array in array: [ [ [1, 2], [8, 9] ], [ ["a", "b" ], ["y", "z"] ] ]
+
+## OBJECTS ##
+
+# Objects hold fields.
+# Just like arrays, fields in objects can be separated with commas..
+object: { key: value, another_key: another_value }
+server_connection: {ip: "127.0.0.1", port: 80}
+first: {letter: a, number: 1,} # Notice the trailing comma.
+# or newlines..
+power_grid: {
+ max_capacity: 15000
+ current_power: 1200
+}
+# or both!
+food_colors: {
+ carrot: orange,
+ pear: green,
+ apple: red,
+ plum: purple,
+ banana: yellow, # Trailing commas are okay here too!
+}
+
+# Arrays can hold objects just like any other value type.
+coworkers: [
+ {
+ name: Jeff
+ age: 27
+ },
+ {
+ name: Henry
+ age: 35
+ },
+ {
+ name: Timmy
+ age: 12
+ }
+]
+
+# The field separator may be omitted if the key is followed by {
+no_separator {
+ key: value
+ speed_of_light: very fast
+ ten: 10
+
+ # Objects can go inside other objects just like any other value.
+ another_object {
+ twenty: 20
+ speed_of_sound: also pretty fast
+ }
+}
+
+# In fact, the entirety of any HOCON document is an actually just an object.
+# That object is called the root object. The only difference between it and any
+# other object is that the curly brackets at the top and bottom of the document
+# may be omitted.
+
+# This means that HOCON documents can be formatted in the same way that
+# regular objects can be formatted, including separating fields with commas
+# rather than with newlines.
+
+# Additionally, while the entirety of a HOCON document can be and is usually an
+# object, it can also be an array. If it is an array, the opening and closing
+# brackets at the top and bottom of the document must be explicitly written.
+
+######################
+### DUPLICATE KEYS ###
+######################
+
+is_happy: false
+# If there is a duplicate key, the new value overrides the previous value.
+is_happy: true
+online_users: [Jacob, Mike]
+# Same with arrays.
+online_users: [Jacob, Mike, Henry]
+
+# For objects, it's a bit different.
+my_car: {
+ color: blue
+ speed: 9001
+ passengers: null
+
+ engine: {
+ running: true
+ temperature: 137
+ }
+}
+# If there is a duplicate key and both values are objects,
+# then the objects are merged.
+my_car: {
+ // These fields are added to the old, previous object.
+ nickname: "My Favorite Car"
+ type: 2-door sedan
+
+ // Since the value of this duplicate key is NOT an object,
+ // it simply overrides the previous value.
+ speed: 60
+ // Same with arrays. They override, not merge.
+ passengers: ["Nate", "Ty"]
+
+ // This object is recursively merged with the other object.
+ engine: {
+ // These two fields are added to the previous object.
+ type: gas
+ oil_level: 10
+ // This field overrides the previous value.
+ temperature: 179
+ }
+}
+
+# Object merging is done two at a time. That is to say, the first two objects
+# merge into one, then that object merges with the next object, and so on.
+
+# Because of this, if you set a field with an object value to a non-object value
+# and then back to an object value, the new object will completely override any
+# previous value.
+
+// Null, a non-object value, completely overrides the object value.
+my_car: null
+
+// Then, this object completely overrides null.
+my_car: {
+ nickname: "My New Car"
+ type: 4-door minivan
+ color: gray
+ speed: 90
+ passengers: ["Ayden", "Liz"]
+}
+
+###########################
+### VALUE CONCATENATION ###
+###########################
+
+## SIMPLE VALUE CONCATENATION ##
+
+# Simple values (all value types except objects and arrays) separated by
+# whitespace are concatenated into a single string. The whitespace between
+# values is preserved.
+number_concatenation: 1 2 3 12.5 -3 2e5 // same as: "1 2 3 12.5 -3 2e5"
+boolean_concat: true false true // "true false true"
+null_concat: null null null // "null null null"
+mixed_concat: 1 true null // "1 true null"
+
+# String value concatenation can appear anywhere that a quoted string can.
+number_concat_in_array: [1 2, 3 4, 5 6] // same as: ["1 2", "3 4", "5 6"]
+
+# In fact, unquoted strings are actually just string value concatenations.
+unquoted_string_concat: his name is jeff // same as: "his name is jeff"
+
+# Going further, even keys that are unquoted strings are actually just string
+# value concatenations.
+this is a key: value // the KEY is the same as: "this is a key"
+# The following field is identical to the field above.
+"this is a key": value
+
+# Quoted strings can also be concatenated. This will be useful later,
+# when we cover substitutions.
+quoted_string_concat: "her"" name" "is ""jenna" // same as: "her name is jenna"
+# Notice that the whitespace (or lack thereof) between values is preserved.
+
+## ARRAY CONCATENATION ##
+
+# Arrays separated by whitespace are merged into a single array.
+array_concat: [1, 2, 3] [4, 5, 6] // same as: [1, 2, 3, 4, 5, 6]
+
+# Arrays cannot be concatenated with a non-array value.
+//array_concat: true [false] results in an error
+//array_concat: 1 [2] results in an error
+
+## OBJECT CONCATENATION ##
+
+# Objects separated by whitespace are merged into a single object.
+# The merge functionality is identical to that of duplicate key object merging.
+lamp: {on: true} {color: tan} // same as: {on: true, color: tan}
+
+# Similarly to arrays, objects cannot be concatenated with a non-object value.
+//object_concat: true {on: false} results in an error
+//object_concat: 1 {number: 2} results in an error
+
+########################
+### PATH EXPRESSIONS ###
+########################
+
+# Path expressions are used to write out a path through the object graph.
+# Think of it as navigating through objects to a specific field.
+# Each object to traverse through is called an element, and each element is
+# separated with a period.
+
+country: {
+ city: {
+ neighborhood: {
+ house: {
+ name: "My House"
+ address: 123 Example Dr.
+ }
+ }
+ }
+}
+# For example, the path to the address of the house could be written as:
+# country.city.neighborhood.house.address
+# Country, city, neighborhood, house, and address are all elements.
+
+# Path expressions are used in two places: substitutions (which will be
+# covered in a moment), and as keys.
+# That's right: keys themselves can also be path expressions.
+foo: {
+ bar: {
+ baz: {
+ number: 12
+ }
+ }
+}
+# Rather than tediously specifying each object, a path expression can be used.
+# The following field represents the same object found above.
+foo.bar.baz.number: 12
+
+# Fields and objects specified with path expressions are merged in the same way
+# that any object is usually merged.
+foo.bar.baz.bool: true
+// the object foo's value is: foo { bar { baz { number: 12, bool: true } } }
+
+#####################
+### SUBSTITUTIONS ###
+#####################
+
+# Substitutions refer to a specific value from some path expression.
+# They're only allowed in values, not keys or nested inside other substitutions.
+
+me: {
+ favorite_animal: parrots
+ favorite_food: cookies
+}
+# The syntax for a substitution is either ${path_expression} or
+# ${?path_expression}. The latter syntax will be discussed in a moment.
+my_fav_animal: ${me.favorite_animal}
+my_fav_food: ${me.favorite_food}
+
+# Substitutions are not parsed inside quoted strings. To get around this,
+# either use an unquoted string or value concatenation.
+animal_announcement: My favorite animal is ${my_fav_animal}
+// the value is: My favorite animal is parrots
+food_announcement: "My favorite food is "${my_fav_food}"!"
+// the value is: "My favorite food is cookies!"
+
+# Substitutions are parsed last in the document. Because of this, you can
+# reference a key that hasn't been defined yet.
+color_announcement: "My favorite color is" ${my_fav_color}"!"
+// the value is: "My favorite color is blue!"
+my_fav_color: blue
+
+# Another effect of substitutions being parsed last is that substitutions will
+# always use the latest, as in last, value assigned in the entire document,
+# which includes merged objects.
+color: green
+their_favorite_color: ${color} // the value is: orange
+color: orange
+
+random_object: {
+ number: 12
+}
+the_number: ${random_object.number} // the value is: 15
+random_object: {
+ number: 15
+}
+
+###############################
+### UNDEFINED SUBSTITUTIONS ###
+###############################
+
+# A substitution using the ${path_expression} syntax with an undefined path
+# expression, meaning a path expression that does not point to a defined value,
+# is invalid and will therefore generate an error.
+//${does.not.exist} will throw an error
+
+# However, an undefined substitution using the ${?path_expression} syntax
+# has different behavior depending on what it is the value of.
+request: {
+ # If it is the value of a field, then the field will not be created.
+ response: ${?does.not.exist} // this field won't be created and does not exist
+ type: HTTP
+}
+
+request: {
+ # Additionally, if it would have overridden a previous value, then the
+ # previous value remains unchanged.
+ type: ${?does.not.exist} // request.type is still HTTP
+}
+
+# If it is a value in an array, then it is simply not added.
+values: [ 172, "Brian", ${?does.not.exist}, null, true, ]
+// the value is: [ 172, "Brian", null, true ]
+
+# If it is part of simple value concatenation, it acts as an empty string.
+final_string: "String One"${?does.not.exist}"String Two"
+// the value is: "String OneString Two"
+
+# If it is part of array concatenation, it acts as an empty array.
+final_array: [ 1, 2, 3 ] ${?does.not.exist} [ 7, 8, 9 ]
+// the value is: [ 1, 2, 3, 7, 8, 9 ]
+
+# If it is part of object concatenation, it acts as an empty object.
+final_array: { a: 1 } ${?does.not.exist} { c: 3 }
+// the value is: { a: 1, c: 3 }
+
+######################################
+### SELF-REFERENTIAL SUBSTITUTIONS ###
+######################################
+
+# Substitutions normally "look forward" and use the final value defined in the
+# document. However, in cases when this would create a cycle, the substitution
+# looks only backwards.
+
+# A field which contains a substitution that points to itself or points to
+# other fields that eventually point back to itself is called a
+# self-referential field.
+letters: "a b c" // the value is: "a b c"
+letters: ${letters}" d" // "a b c d"
+letters: ${letters}" e" // "a b c d e"
+
+PATH: [/bin] // the value is: [/bin]
+PATH: ${PATH} [/usr/bin] // [/bin, /usr/bin]
+PATH: ${PATH} [/usr/local/bin] // [/bin, /usr/bin, /usr/local/bin]
+
+x: "x" // the value is: "x"
+y: ${x}"y" // "xy"
+x: ${y}"z" // "xyz"
+
+##########################
+### += FIELD SEPARATOR ###
+##########################
+
+# In addition to : and =, there actually exists another separator: +=
+# A field separated with += acts as a self-referential array concatenation.
+# In short, it appends an element to a previously defined array.
+
+a: [1]
+b: [1]
+# This field:
+a += 2 // the value is: [1, 2]
+# functions the same as:
+b: ${?b} [2] // the value is: [1, 2]
+
+USERS: [/usr/luke] // the value is: [/usr/luke]
+USERS += /usr/devon // [/usr/luke, /usr/devon]
+USERS += /usr/michael // [/usr/luke, /usr/devon, /usr/michael]
+
+# Since += only appends elements to a previously existing array, if the previous
+# value was not an array, an error will be generated.
+OTHER_USERS: /usr/luke
+//OTHER_USERS += /usr/devon results in an error
+
+# Notice that the underlying substitution syntax used is ${?path}, not ${path}.
+# Recall that, using the ${?} syntax, an undefined substitution in array
+# concatenation acts as an empty array. Because of this, it is perfectly
+# acceptable if the field that is being set is initially undefined.
+//z: [] not necessary
+z += 3 // the value is: [3]
+z += 4 // the value is: [3, 4]
+
+NEW_USERS += /usr/sandra // the value is: [/usr/sandra]
+NEW_USERS += /usr/kennedy // [/usr/sandra, /usr/kennedy]
+NEW_USERS += /usr/robin // [/usr/sandra, /usr/kennedy, /usr/robin]
+
+################
+### INCLUDES ###
+################
+
+# Includes allow you to "import" one HOCON document into another.
+
+# An include statement consists of the unquoted string "include" followed by
+# whitespace and then a resource name, which is one of the following:
+# - a single quoted string which is heuristically interpreted as a URL,
+# filename, or a Java classpath resource.
+# - url(), file(), or classpath(), with the parentheses surrounding a quoted
+# string which is either a URL, filename, or classpath resource respectively.
+# - required(), with the parentheses surrounding one of the above.
+include "https://example.com/config.conf"
+include "/foo/bar/config.conf"
+include "config.conf"
+
+include url("https://example.com/config.conf")
+include file("/foo/bar/config.conf")
+include classpath("config.conf")
+
+# If the included file does not exist, it will be silently ignored and act as if
+# it were an empty object. However, if it is wrapped around required(), then
+# parsing will explicitly error if the file cannot be resolved.
+//include required("doesnt_exist.conf") will error
+//include required(url("https://example.com/doesnt_exist.conf")) will error
+//include required(file("doesnt_exist.conf")) will error
+//include required(classpath("doesnt_exist.conf")) will error
+
+# The file specified by the include statement is called the included file, and
+# the file which contains the include statement is called the including file.
+
+# Including a file functions as if you directly replaced the include statement,
+# wherever it may be, with the contents of the included file's root object.
+
+# An included file must have an object as its root value and not an array.
+# If the included file has an array as its root value, then it is invalid and
+# an error will be generated.
+
+# Pretend that the following is in a file called user_config.conf:
+username: RandomUser1337
+auto_login: true
+color_theme: dark
+screensaver: {
+ image: usr/images/screensaver.jpg
+ turn_on_after: 1m
+}
+
+# And then we include that file.
+include file("user_config.conf")
+
+# We can now reference values from that file!
+path_to_user_screensaver: ${screensaver.image} //
+greeting: "Welcome, "${username}"!" // the value is: "Welcome, RandomUser1337!"
+
+# Duplicate keys override as they normally do.
+status: "Auto Login: "${auto_login} // the value is: "Auto Login: true"
+auto_login: false
+status: "Auto Login: "${auto_login} // the value is: "Auto Login: false"
+
+# Object merging is also the same as usual.
+screensaver: {
+ // This gets added to the screensaver object.
+ enable_during_day: false
+ // This overrides the previous value.
+ turn_on_after: 30s
+}
+
+# Include statements can appear in place of a field. Anywhere that a field
+# could appear, an include statement could appear as well.
+
+# Pretend that the following is in a file called server_settings.conf:
+max_connections: 10
+url: example.com
+port: 80
+admin_page: {
+ username: admin
+ password: pass12345
+}
+
+# And then we include that file nested inside another object.
+websites: {
+ my_epic_website: {
+ include file("server_settings.conf")
+ }
+}
+
+# Now, we can reference the contents of server_settings.conf as if they
+# had been written directly into the object my_epic_website.
+server_port: ${websites.my_epic_website.port}
+
+the_password: "The password is: "${websites.my_epic_website.admin_page.password}
+// the value is: The password is: pass12345
+
+max_conn: "Max Connections: "${websites.my_epic_website.max_connections}
+// the value is: Max Connections: 10
+```
+
+### More Resources
+
++ [Official HOCON Specification](https://github.com/lightbend/config/blob/master/HOCON.md)
++ [HOCON Playground](https://hocon-playground.herokuapp.com)